How to achieve Place Search and Marker Clustering Implementation in Map App
Tags: java, android, tutorial, opensource
Published: 2021-11-12 09:30:17

## Background
Lots of apps these days include an in-app map feature and the ability to mark places of interest on the map. HMS Core Map Kit enables you to implement such capabilities for your apps. With Map Kit, you can first draw a map and then add markers to the map, as well as configure the map to cluster markers depending on the level of zoom. This article will show you how to implement searching for nearby places using the keyword search capability of Site Kit and display the results on the map. 

**Application scenarios:**
1. A travel app that allows users to search for scenic spots and shows the results on a map.
2.A bicycle sharing app that can show users nearby bicycles on a map. 

**Key functions used in the project:**
- Location service: Use Location Kit to obtain the current longitude-latitude coordinates of a device.
- Keyword search: Use Site Kit to search for places such as scenic spots, businesses, and schools in the specified geographical area based on the specified keyword.
- Map display: Use Map Kit to draw a map. 
Marker clustering: Use Map Kit to add markers to the map and configure the map to cluster markers depending on the level of zoom. 

## Integration Preparations
**1.Register as a developer and create a project in AppGallery Connect.**
(1)Register as a developer
[Registration URL](https://developer.huawei.com/consumer/en/service/josp/agc/index.html?ha_source=hms1)
![Register as a developer](https://oscimg.oschina.net/oscnet/up-3a20707d23aca0fb5d402c1126db5798962.png)
(2)Create an app, add the SHA-256 signing certificate fingerprint, enable Map Kit and Site Kit, and download the agconnect-services.json file of the app.
![Create an app](https://oscimg.oschina.net/oscnet/up-393d3780968aed9f087d7ff432be0eccae7.png)

**2.Integrate the Map SDK and Site SDK.** 
(1)Copy the agconnect-services.json file to the app's directory of your project.
- Go to allprojects > repositories and configure the Maven repository address for the HMS Core SDK.
- Go to buildscript > repositories and configure the Maven repository address for the HMS Core SDK.
- If the agconnect-services.json file has been added to the app, go to buildscript > dependencies and add the AppGallery Connect plugin configuration.

```java
buildscript {
    repositories {
        maven { url 'https://developer.huawei.com/repo/' }
        google()
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.3.2'
        classpath 'com.huawei.agconnect:agcp:1.3.1.300'
    }
}
allprojects {
    repositories {
        maven { url 'https://developer.huawei.com/repo/' }
        google()
        jcenter()
    }
}
```
(2)Add build dependencies in the dependencies block.
```java
dependencies {
    implementation 'com.huawei.hms:maps:{version}'
    implementation 'com.huawei.hms:site:{version}'
 implementation 'com.huawei.hms:location:{version}'
}
```
(3)Add the following configuration to the file header:
```java
apply plugin: 'com.huawei.agconnect'
```
(4)Copy the signing certificate generated in [Generating a Signing Certificate](https://developer.huawei.com/consumer/en/doc/development/HMSCore-Guides-V5/android-sdk-config-agc-0000001061560289-V5) to the app directory of your project, and configure the signing certificate in android in the build.gradle file.
```java
signingConfigs {
    release {
        // Signing certificate.
            storeFile file("**.**")
            // KeyStore password.
            storePassword "******"
            // Key alias.
            keyAlias "******"
            // Key password.
            keyPassword "******"
            v2SigningEnabled true
        v2SigningEnabled true
    }
}
buildTypes {
    release {
        minifyEnabled false
        proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        debuggable true
    }
    debug {
        debuggable true
    }
}
```
## Main Code and Used Functions
**1.Use Location Kit to obtain the device location.**
```java
private void getMyLoction() {

        fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(this);
        SettingsClient settingsClient = LocationServices.getSettingsClient(this);
        LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder();
        mLocationRequest = new LocationRequest();
        builder.addLocationRequest(mLocationRequest);
        LocationSettingsRequest locationSettingsRequest = builder.build();
//Check the device location settings.
        settingsClient.checkLocationSettings(locationSettingsRequest)
                .addOnSuccessListener(new OnSuccessListener<LocationSettingsResponse>() {
                    @Override
                    public void onSuccess(LocationSettingsResponse locationSettingsResponse) {
                        //Initiate location requests when the location settings meet the requirements.
                        fusedLocationProviderClient
                                .requestLocationUpdates(mLocationRequest, mLocationCallback, Looper.getMainLooper())
                                .addOnSuccessListener(new OnSuccessListener<Void>() {
                                    @Override
                                    public void onSuccess(Void aVoid) {
                                        // Processing when the API call is successful.
                                        Log.d(TAG, "onSuccess: " + aVoid);
                                    }
                                });
                    }
                })
```
**2.Implement the text search function using Site Kit to search for nearby places.**
```java
SearchResultListener<TextSearchResponse> resultListener = new SearchResultListener<TextSearchResponse>() {
    // Return search results upon a successful search.
    @Override
    public void onSearchResult(TextSearchResponse results) {
        List<Site> siteList;
        if (results == null || results.getTotalCount() <= 0 || (siteList = results.getSites()) == null
                || siteList.size() <= 0) {
            resultTextView.setText("Result is Empty!");
            return;
        }
 updateClusterData(siteList);// Mark places on the map.
    }

    // Return the result code and description upon a search exception.
    @Override
    public void onSearchError(SearchStatus status) {
        resultTextView.setText("Error : " + status.getErrorCode() + " " + status.getErrorMessage());
    }
};
// Call the place search API.
searchService.textSearch(request, resultListener);
```
**3.Draw a map**
```java
    @Override
    public void onMapReady(HuaweiMap huaweiMap) {
        hMap = huaweiMap;
        hMap.moveCamera(CameraUpdateFactory.newLatLngZoom(Constants.sMylatLng, 1));
        hMap.setMyLocationEnabled(true);
        hMap.getUiSettings().setMyLocationButtonEnabled(true);
        initCluster(huaweiMap);

    }
```
**4.Cluster markers on the map.**
```java
private ClusterManager<MyItem> mClusterManager;
List<MyItem> items = new ArrayList<>();

private void initCluster(HuaweiMap hMap) {
    mClusterManager = new ClusterManager<>(this, hMap);
    hMap.setOnCameraIdleListener(mClusterManager);
    // Add a custom InfoWindowAdapter by setting it to the MarkerManager.Collection object from
    // ClusterManager rather than from GoogleMap.setInfoWindowAdapter
//refer: https://github.com/billtom20/3rd-maps-utils  
    mClusterManager.getMarkerCollection().setInfoWindowAdapter(new HuaweiMap.InfoWindowAdapter() {
        @Override
        public View getInfoWindow(Marker marker) {
            final LayoutInflater inflater = LayoutInflater.from(SearchClusterActivity.this);
            final View view = inflater.inflate(R.layout.custom_marker_window, null);
            final TextView textView = view.findViewById(R.id.textViewTitle);
            String text = (marker.getTitle() != null) ? marker.getTitle() : "Cluster Item";
            textView.setText(text);
            return view;
        }

        @Override
        public View getInfoContents(Marker marker) {
            return null;
        }
    });
   
}
     // Update clustered markers.
private void updateClusterData(List<Site> siteList) {
    items  = new ArrayList<>();
    mClusterManager.clearItems();
    for (Site s:
            siteList) {
        Coordinate location = s.getLocation();
        MyItem myItem = new MyItem(location.lat,location.lng,s.name,s.formatAddress);
        items.add(myItem);
    }
    mClusterManager.addItems(items);
    Coordinate coordinate =  siteList.get(0).getLocation();
    LatLng latLng = new LatLng(coordinate.lat,coordinate.lng);
    mClusterManager.cluster();
    hMap.animateCamera(CameraUpdateFactory.newLatLngZoom (latLng,14 ));
}
```
## Results
Enter a place or service in the Query box and tap Search. The figures below show how the search results are displayed as markers on the map. 
![Map1](https://oscimg.oschina.net/oscnet/up-9f58de2c72f21a541474f50dcd907eecf04.png)
![Map2](https://oscimg.oschina.net/oscnet/up-bdbde00ba8bc57a794bf3e29ae755e64e4c.png)
The preceding four figures show the effect of searching for food and school, as well as the marker clustering effect at different zoom levels. Congratulations, you have now successfully integrated place search and marker clustering into your in-app map.

## References
For more details, you can go to:[official website](https://developer.huawei.com/consumer/en/hms/huawei-MapKit/?ha_source=hms1)
[Development Documentation page](https://developer.huawei.com/consumer/en/doc/development/HMSCore-Guides/android-sdk-introduction-0000001061991291?ha_source=hms1), to find the documents you need
[Reddit](https://www.reddit.com/r/HMSCore/) to join our developer discussion
[GitHub](https://github.com/HMS-Core/hms-mapkit-demo) to download sample codes
[Stack Overflow](https://stackoverflow.com/questions/tagged/huawei-mobile-services?tab=Votes) to solve any integration problems