Skip to content

Annotation Rendering

Annotations

In order to add annotations to the Map, the user needs to create an Annotation object first. The Annotation class contains the interface that clients use to configure the display properties of the annotation. The information required to render an annotation is:

  • Location: The position of the annotation in the map, using Android's Location class.
  • Graphic: The graphic info, if any, used to present the image for the annotation. This can be set via resource ID, Drawable, and a variety of other methods. (This information is optional, but the annotation requires either Graphic or TextDisplayInfo.)
  • TextDisplayInfo: The text display info, if any, used to present textual information for the annotation. (This information is Optional, but requires either Graphic or TextDisplayInfo.)

All annotations are instantiated via a Factory before client adds additional configuration and passes them to the MapView for rendering.

How to create an annotation:

Here is sample code for how to create annotations to MapView.

1
2
3
4
5
6
7
//Create Annotation with resource ID
Location location = new Location("");
location.setLatitude(37.402295);
location.setLongitude(-122.059237);
Annotation annotation = AnnotationFactory.create(context, R.drawable.map_pin_green_icon_unfocused, location);

//Any additional settings here...
1
2
3
4
5
6
7
//Create Annotation from resource ID
val location = new Location("")
location.latitude  = 37.402295
location.longitude = -122.059237
val annotation = AnnotationFactory.create(context, R.drawable.map_pin_green_icon_unfocused, location)

//Any additional settings here...

How to add annotations:

Here is sample code for how to add annotations to MapView.

1
2
3
4
5
6
7
8
9
Annotation annotation = ...; // Previously created annotation

//Any additional settings here...

//Create list of annotations
List<Annotation> annotations = new ArrayList<>(Arrays.asList(annotation));

//Note: Must be done from main thread!
mapView.annotationsController().add(annotations);
1
2
3
4
5
6
7
8
9
val annotation = ...; // Previously created annotation

//Any additional settings here...

//Create list of annotations
val annotations = arrayListOf(annotation)

//Note: Must be done from main thread!
mapView.annotationsController().add(annotations);

How to remove annotations:

Here is sample code for how to remove annotations from MapView.

1
2
3
4
5
6
7
Annotation annotation = ...; // Previously created annotation

//Create list of annotations
List<Annotation> annotations = new ArrayList<>(Arrays.asList(annotation));

//Note: Must be done from main thread!
mapView.annotationsController().remove(annotations)
1
2
3
4
5
6
7
val annotation = ... // Previously created annotation

//Create list of annotations
val annotations = arrayListOf(annotation)

//Note: Must be done from main thread!
mapView.annotationsController().remove(annotations)

Show POI on map

There are some easy APIs for users to show point of interest (POI) on map. These APIs will help users to: * Mapping POIs to Annotations. * Manage the cache of POIs. * Add an annotation when the location of the POI is within the screen. * Remove an annotation when the location of the POI is out of the screen.

The information required to show POIs is:

  • SearchController: The controller of POIs and related annotations.
  • SearchEngine: The search engine used to search POIs on the screen.
  • PoiAnnotationFactory: The factory of Annotation. Users can create their own annotation styles with POI by implementing a custome factory.

How to display POI on map:

Here is sample code for how to initialize searchController.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
SearchController searchController = mapView.searchController();

// Inject a search engine
searchController.injectSearchEngine(new NavSearchEngine(context));

// Inject an annotation factory. Optional settings
searchController.injectPoiAnnotationFactory(new NavPoiFactory(context, mapView));

public class NavSearchEngine implements SearchEngine {
    Location cvpLocation;
    private Annotation.UserGraphic gasGraphic;
    private Annotation.UserGraphic foodGraphic;
    private Annotation.UserGraphic defaultGraphic;

    private static final String TAG = "SearchEngine";
    private static final String SEARCH_FOOD = "248";
    private static final String SEARCH_ELECTRIC_CHARGE_STATION = "771";

    public NavSearchEngine(Context context) {
        this.gasGraphic = new Annotation.UserGraphic(BitmapFactory.decodeResource(context.getResources(),
                R.drawable.baseline_local_gas_station_24));
        this.foodGraphic = new Annotation.UserGraphic(BitmapFactory.decodeResource(context.getResources(),
                R.drawable.baseline_restaurant_24_day));
        this.defaultGraphic = new Annotation.UserGraphic(BitmapFactory.decodeResource(context.getResources(),
                R.drawable.defaultBitmap));
    }

    @Nullable
    @Override
    public List<PoiSearchEntity> search(@NotNull List<String> displayContent, @NotNull List<? extends GeoPoint> searchBox,
                                        int maxResultCount, @NotNull String language) throws TimeoutException, Exception {
        Polygon polygon = Polygon.builder().setPoints(new ArrayList<>(searchBox)).build();
        PolygonGeoFilter geoFilter = PolygonGeoFilter.builder(polygon).build();
        CategoryFilter categoryFilter = CategoryFilter.builder()
                .setCategories(displayContent)
                .build();
        SearchFilters searchFilters = SearchFilters.builder()
                .setCategoryFilter(categoryFilter)
                .setGeoFilter(geoFilter)
                .build();
        EntityResponse<Entity> searchResult =  EntityService.getClient().searchRequest()
                .setFilters(searchFilters)
                .setLimit(maxResultCount)
                .setLocation(cvpLocation.getLatitude(), cvpLocation.getLongitude())
                .execute();
        if (searchResult.getResults() != null) {
            throw new Exception("Search category fail");
        }

        List<PoiSearchEntity> result = new ArrayList<>();
        for (Entity entity : searchResult.getResults()) {
            PoiSearchEntity poiSearchEntity = wrapperToPoiSearchEntity(entity);
            if (poiSearchEntity != null) {
                result.add(poiSearchEntity);
            }
        }
        return result;
    }

    @Nullable
    private PoiSearchEntity wrapperToPoiSearchEntity(Entity entity) {
        Location geoLocation;
        String category;
        switch (entity.getType()) {
            case ADDRESS:
                geoLocation = new Location(TAG);
                geoLocation.setLatitude(entity.getAddress().getGeoCoordinates().getLatitude());
                geoLocation.setLongitude(entity.getAddress().getGeoCoordinates().getLongitude());
                category = "";
                break;
            case PLACE :
                geoLocation = new Location(TAG);
                geoLocation.setLatitude(entity.getPlace().getAddress().getGeoCoordinates().getLatitude());
                geoLocation.setLongitude(entity.getPlace().getAddress().getGeoCoordinates().getLongitude());
                List<Category> categories = entity.getPlace().getCategories();
                if (categories != null && !categories.isEmpty()) {
                    category = categories.get(categories.size() - 1).getId();
                } else {
                    category =  "";
                }
                break;
            default:
                return null;
        }

        Annotation.UserGraphic userGraphic;
        switch (category) {
            case SEARCH_ELECTRIC_CHARGE_STATION:
                userGraphic = gasGraphic;
                break;
            case SEARCH_FOOD:
                userGraphic = foodGraphic;
                break;
            default:
                userGraphic = defaultGraphic;
        }

        return new PoiSearchEntity(geoLocation, userGraphic, new Bundle());
    }
}

public class NavPoiFactory implements PoiAnnotationFactory {
    private final Context context;
    private final  MapView mapView;
    private Annotation.UserGraphic gasGraphic;

    public NavPoiFactory(Context context, MapView mapView) {
        this.context = context;
        this.mapView = mapView;
        gasGraphic = new Annotation.UserGraphic(BitmapFactory.decodeResource(context.getResources(),
                R.drawable.baseline_local_gas_station_24));

    }

    @NotNull
    @Override
    public Annotation create(@NotNull PoiSearchEntity entity, @NotNull MapMode mapMode) {
        Annotation annotation = mapView.annotationsController().factory().create(context, gasGraphic, entity.getLocation());
        annotation.setStyle(Annotation.Style.ScreenAnnotationFlagNoCulling);
        return annotation;
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
val searchController = mapView.searchController()

// Inject a search engine
searchController.injectSearchEngine(NavSearchEngine(requireContext()))

// Inject an annotation factory. Optional settings.
searchController?.injectPoiAnnotationFactory(NavPoiFactory(requireContext(), mapView))


class NavSearchEngine(val context: Context) : SearchEngine {
    var cvpLocation: Location? = null
    val gasGraphic = Annotation.UserGraphic(BitmapFactory.decodeResource(context.resources, R.drawable.baseline_local_gas_station_24))
    val foodGraphic = Annotation.UserGraphic(BitmapFactory.decodeResource(context.resources, R.drawable.baseline_restaurant_24_night))
    val defaultGraphic = Annotation.UserGraphic(BitmapFactory.decodeResource(context.resources, R.drawable.default_bitmap))

    companion object {
        const val TAG = "NavSearchEngine"
        // Category index
        const val SEARCH_FOOD = "248"
        const val SEARCH_ELECTRIC_CHARGE_STATION = "771"
    }

    override fun search(displayContent: List<String>, searchBox: List<GeoPoint>, resultSize: Int, language: String) : List<PoiSearchEntity>?{
        val polygon = Polygon
                .builder()
                .setPoints(searchBox)
                .build()
        val geoFilter = PolygonGeoFilter
                .builder(polygon)
                .build()
        val categoryFilter = CategoryFilter.builder()
                .setCategories(displayContent)
                .build()
        val searchFilters = SearchFilters.builder()
                .setCategoryFilter(categoryFilter)
                .setGeoFilter(geoFilter)
                .build()
        val entityClient = EntityService.getClient()
        val result = entityClient.searchRequest()
                .setFilters(searchFilters)
                .setLimit(resultSize)
                .setLocation(cvpLocation!!.latitude, cvpLocation!!.longitude)
                .execute()
        return result.results?.mapNotNull { entity ->
            wrapperToPoiSearchEntity(entity)
        }
    }

    private fun wrapperToPoiSearchEntity(entity: Entity): PoiSearchEntity? {
        val geoLocation: Location
        val category: String
        when (entity.type) {
            EntityType.ADDRESS -> {
                geoLocation = Location(TAG).apply {
                    this.latitude = entity.address.geoCoordinates.latitude
                    this.longitude = entity.address.geoCoordinates.longitude
                }

                category = ""
            }
            EntityType.PLACE -> {
                geoLocation = Location(TAG).apply {
                    this.latitude = entity.place.address.geoCoordinates.latitude
                    this.longitude = entity.place.address.geoCoordinates.longitude
                }

                category = entity.place.categories.lastOrNull()?.id ?: ""
            }
            else -> {
                return null
            }
        }

        val bitmap = when(category) {
            SEARCH_ELECTRIC_CHARGE_STATION -> gasGraphic
            SEARCH_FOOD -> foodGraphic
            else -> defaultGraphic
        }

        return PoiSearchEntity(geoLocation, bitmap, Bundle())
    }
}

class NavPoiFactory(val context: Context, val mapView: MapView) : PoiAnnotationFactory {
    private val gasGraphic = Annotation.UserGraphic(BitmapFactory.decodeResource(context.resources, R.drawable.baseline_local_gas_station_24))

    override fun create(entity: PoiSearchEntity, mode: MapMode): Annotation {
        return mapView.annotationsController().factory().create(context, gasGraphic, entity.location).apply {
            this.style = Annotation.Style.ScreenAnnotationFlagNoCulling
        }
    }
}

Here is sample code for how to display POIs.

1
2
3
4
String SEARCH_FOOD_CATEGORY = "248";
List<String> categorys = new ArrayList();
categorys.add(SEARCH_FOOD_CATEGORY);
searchController.displayPOI(categorys);
1
2
val SEARCH_FOOD_CATEGORY = "248"
searchController?.displayPOI(listOf(SEARCH_FOOD_CATEGORY))

How to hide POI on map:

Here is sample code for how to stop showing POIs.

1
searchController.displayPOI(new ArrayList<String>());
1
searchController?.displayPOI(emptyList())