The core feature of the Sygic Android SDK is Maps. The key concepts covered in this section include adding a map to an Android application, changing the location displayed by the map and its various properties. The classes covered include MapFragment and MapView. The MapFragment is a standard Android Fragment derived component. MapView is a class for controlling the displayed map. To obtain an instance of MapView, call the asynchronous MapFragment.getMapAsync():

mapFragment.getMapAsync(new OnMapInitListener() {
    @Override
    public void onMapInitializationInterrupted() {}

    @Override
    public void onMapReady(@NonNull final MapView mapView) {
        // now you can use returned MapView
    }
});

Once the map is ready, you can get the MapView synchronously, using the MapFragment.getMapView(). For example, the following code snippet illustrates how to render the Map at London, UK:

// get MapView
MapView map = mapFragment.getMapView();

// Set the map center to London, UK.
map.getCameraModel().setPosition(new GeoCoordinates(51.509865, -0.118092));

To enable online maps, you need to get the OnlineManager using the OnlineManagerProvider.getInstance() and then call enableOnlineMapStreaming on the onlineManager instance. To display offline maps, see Offline Maps

In the preceding code the GeoCoordinates for the map center is created by a call to the new GeoCoordinates(double, double) constructor.

GeoBoundingBox defines a rectangular area in a geographic coordinate system. It is defined by the GeoCoordinates of its left top corner and of its right bottom corner. For example a bounding box of London would be defined like this :

val topLeft = GeoCoordinates(51.692701, -0.561623)
val bottomRight = GeoCoordinates(51.303139, 0.380848)
val boundingBox = GeoBoundingBox(topLeft, bottomRight)

FPS Configuration
You can set a custom FPS (frames per second) configuration. You can choose between two modes - Balaced and Performance. These two modes are affected by the targetFps value.
If you choose the Performance mode, the engine always tries to achieve the targetFps value. If you choose the Balanced mode, the targetFps value is the lowest the engine should go in order to preserve battery life. Let's see how to set the FpsConfig for the map to always try to run at 60fps:

mMapView.fpsLimit = FpsConfig(FpsConfig.FpsMode.PERFORMANCE, 60f)

Map Handling

Once the MapView is initialized, it can be manipulated and interacted in a variety of ways. Some key attributes of the MapView are its orientation, tilt, geographical center (geoCenter), and zoom level (zoom). You can use these to customize the displayed map via the Camera class.

Zoom Level

The size of the geographical area displayed by Map can be controlled by changing the zoom level. The values can be in the interval between 0 and 20.

// Set the zoom to for example 18.5.
map.getCameraModel().setZoomLevel(18.5f);

// Get the zoom level, should be 18.5.
float zoom = map.getCameraModel().getZoomLevel();

Rotation

The map can be orientated in any direction. By default, the rotation is in a true North position. The following code changes the orientation to South-up.

// Rotate by 180 degrees.
map.getCameraModel().setRotation(180);

// Get the rotation that you have set above - the returned value should be 180.
float rotation = map.getCameraModel().getRotation();

Tilt

The map can be tilted and rendered in a three-dimensional perspective. By default, the tilt is completely flat.

// Set the tilt to 45 degrees
map.getCameraModel().setTilt(45);

// Get the tilt
float tilt = map.getCameraModel().getTilt();

Camera modes

You can set Camera to change position or rotation automatically with setting an appropriate mode:

map.getCameraModel().setMovementMode(Camera.MovementMode.FollowGpsPosition);
map.getCameraModel().setRotationMode(Camera.RotationMode.Attitude);
Tilt used to display 3D or 2D mode

Camera listeners

If you want to be notified about certain events, you can simply add camera listeners this way:

map.getCameraModel().addModeChangedListener(new Camera.ModeChangedListener() {
    @Override
    public void onMovementModeChanged(@Camera.MovementMode int mode) {
        switch (mode) {
            case Camera.MovementMode.Free:
                //your code
            case Camera.MovementMode.FollowGpsPosition:
                //your code
            case Camera.MovementMode.FollowGpsPositionWithAutozoom:
                //your code
        }
    }

    @Override
    public void onRotationModeChanged(@Camera.RotationMode int mode) {
        switch (mode) {
            case Camera.RotationMode.Free:
            case Camera.RotationMode.NorthUp:
            case Camera.RotationMode.Attitude:
            case Camera.RotationMode.Vehicle:
                //your code
        }
    }
});

or

map.getCameraModel().addPositionChangedListener(new Camera.PositionChangedListener() {
    @Override
    public void onPositionChanged(final GeoCoordinates geoCenter, final float zoom, final float rotation, final float tilt) {
        //your code here
    }

    @Override
    public void onPositionChangeCompleted() {
        //your code here
    }
});

If you no longer need listeners (eg OnDestroy), don't forget to unregister the listeners as well:

map.getCameraModel().removeModeChangedListener(this);
map.getCameraModel().removePositionChangedListener(this);

Default map click handling

Touches on map are handled internally by the MapFragment. If you want to perform an action on click (like displaying a context menu, a bubble on the map, etc...) you have to register a MapGestureListener. We also provide the MapGestureAdapter class that provides default implementations so you can override only the methods that you need.

MapView map = mapFragment.getMapView();
map.addMapGestureListener(new MapGestureAdapter() {

    @Override
    public boolean onMapClicked(final MotionEvent e, final boolean isTwoFingers) {
        // do something...
        return true;
    }
});

This will override only the onMapClicked() method. Calling true as return value will prevent delegating onMapClick to SDK. Then you can get the GeoCoordinates from the place where you tapped on the screen,

final MapView map = mapFragment.getMapView();
GeoCoordinates get = map.geoCoordinatesFromPoint(x, y);

where {x, y} are screen coordinates of point inside of the MapFragment (obtained for example from MotionEvent.getX() and getY()).

Initial map state

By default, the map will show a whole globe after initialization.
If you are adding the fragment dynamically in code, you can use factory method MapFragment.newInstance() and pass it your wanted initial CameraState.

final CameraState.Builder cameraState = new CameraState.Builder();
cameraState.setPosition(new GeoCoordinates(40.730655, -73.997437));
cameraState.setZoomLevel(18.5f);
MapFragment.newInstance(cameraState.build());

Map Schemes

There are some map schemes available so you can offer a different kind of map appearance to your users. The list of available schemes can be obtained by:

final List<String> availableSkins = map.getAvailableSkins();

Some of the options are "default", "day", "night", "car", "pedestrian". The schemes can be combined as they are only incremental. More info on scheme configuration and possibility to create own schemes is coming soon.

map.getMapDataModel().setSkin(Arrays.asList("day", "pedestrian", "default"));
Day skin Night Skin

Sygic SDK does not automatically switch map schemes during navigation mode. Before starting car or pedestrian navigation, be sure to save the current map scheme and switch to the appropriate navigation map scheme.

Gestures

The MapView class responds to a number of predefined touch gestures. The default behavior of the map for each gesture type may be used as-is, supplemented, or replaced entirely. The following table is a summary of the available gestures and their default behavior.

Gesture Description Following gestures
Rotate Rotate: Two-finger press, simultaneously orbit both fingers around the center point, lift Pinch, Spread, Move
Zoom out Pinch: Two-finger press, move inwards, lift Spread, Rotate, Move
Zoom in Spread: Two-finger press, move outwards, lift
Move Swipe: One-finger press, move, lift/don't lift
Tilt Tilt: Two fingers press, move up (increase the tilt angle) or move down (decrease the tilt angle)
Short touch Short touch: One-finger press, lift

Map input like map dragging, map rotation, map tilt, map zoom is handled internally by MapFragment. However you can override default behavior by setting your own listener:

MapView map = mapFragment.getMapView();
map.addMapGestureListener(new MapGestureAdapter() {
    @Override
    public boolean onMapMove(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
        // do something...
        return true;
    }
});

Set the return value to true if the listener has consumed the event, false otherwise. If false is returned, the default gesture action will be executed.

Objects and Interaction

The Sygic Mobile SDK allows the addition of a variety of objects, each with a specific purpose, to a map view. The types of available objects include map markers, routes, polylines, circles, polygons. To see all the objects check the MapObject.MapObjectType. Some objects are described in more detail below.

In order to display the map object on the map, the object needs to be added to a MapView's data model by calling addMapObject(). The objects are initialized using a builder.

MapCircle

Enables you to draw a circle on the map at a fixed geographical location. You can customize you circle's color, radius, lineColor - see the MapCircle API reference for more information.

MapCircle circle = MapCircle.at(new GeoCoordinates(51.509865, -0.118092))
                            .setRadius(200)
                            .build();
map.getMapDataModel().addMapObject(circle);

// you can remove the circle like this
map.getMapDataModel().removeMapObject(circle);
Circle

MapPolyline

Enables you to draw one or more connected line segments on the map. The segment vertices are specified by a series of GeoCoordinates. The visual appearance of the polyline can also be customized. See the MapPolyline API reference for more information.

MapPolyline polyline = MapPolyline.of(Arrays.asList(
                                      new GeoCoordinates(51.509, -0.118),
                                      new GeoCoordinates(51.524, -0.137),
                                      new GeoCoordinates(51.572, -0.092)))
                                  .build();
map.GetMapDataModel().addMapObject(polyline);

// remove the polyline
map.GetMapDataModel().removeMapObject(polyline);
Polyline

MapMarker

With Sygic Mobile SDK, you can display an icon at a fixed geographical position on the map.

MapMarker marker = MapMarker.at(new GeoCoordinates(51.509865, -0.118092)
                                .build();
map.GetMapDataModel().addMapObject(marker);

// remove the marker
map.GetMapDataModel().removeMapObject(marker);
Marker

You can also provide your own image by using the BitmapFactory class. Sdk provides two factories for creating markers from Bitmap or Drawable.

MapMarker marker1 = MapMarker.at(new GeoCoordinates(51.509865, -0.118092)
                                 .withIcon(new DrawableFactory(R.drawable.ic_marker))
                                 .build();

Bitmap bitmap = getBitmap();
MapMarker marker2 = MapMarker.at(new GeoCoordinates(51.515436, -0.122045)
                                 .withIcon(new BitmapFactory(bitmap))
                                 .build();

MapPolygon

A MapPolygon can be displayed on the map since the version 17. You can customize the border/center colors, their radiuses or its center.

ArrayList<GeoCoordinates> geolist = new ArrayList<>();
geolist.add(new GeoCoordinates(48.164498, 17.160628));
geolist.add(new GeoCoordinates(48.141367, 17.099931));
geolist.add(new GeoCoordinates(48.113529, 17.124068));
geolist.add(new GeoCoordinates(48.126849, 17.199407));
geolist.add(new GeoCoordinates(48.145362, 17.164499));

MapPolygon mapPolygon = MapPolygon.of(new GeoCoordinates(48.147267, 17.150949), geolist)
    .setCenterColor(Color.TRANSPARENT)
    .setBorderRadius(0.999999f)
    .setCenterRadius(0.95f)
    .setBorderColor(Color.BLUE)
    .build();
mapDataModel.addMapObject(mapPolygon);

Disabling visibility of objects

It's possible that you would not want to see some of the objects that are shown directly in the map by default, such as 3D buildings, POIs or even roads.
You can simply disable them like this:

mMapView.mapDataModel.setMapLayerCategoryVisibility(MapView.MapLayerCategory.Roads, false) // disables visibility of roads
mMapView.mapDataModel.setMapLayerCategoryVisibility(MapView.MapLayerCategory.Landmarks, false) // disables visibility of 3D buildings (landmarks)

To enable them back again, simply call the same code passing a "true" value.

Requesting objects from map

To query what objects are displayed on map, you can call method MapView.requestObjectsAtPoint(). You can for example use a point returned from onMapClicked().

mapView.addMapGestureListener(new MapGestureAdapter() {
    @Override
    public boolean onMapClicked(final MotionEvent e, final boolean isTwoFingers) {
        mapView.requestObjectsAtPoint(e.getX(), e.getY(), new RequestObjectCallback() {
            @Override
            public void onRequestResult(@NonNull final List<ViewObject> objects, final float x, final float y) {

            }
        });

        return true;
    }
});

In the objects parameter there will be a list of objects, as multiple objects can be displayed in the same area. They can have following types:

ScreenObject

This is the very basic object that will be returned from every call to requestObjectsAtPoint(). It has only GeoCoordinates that can be retrieved using a getPosition() call.

MapObjects

This can be any object mentioned previously in Objects and Interaction that was added to map.

ProxyObjects

These are objects rendered by map, such as Places (previously Points of Interest) (ProxyPlace) or cities (ProxyCity). Additional information about Places can be loaded via ProxyObjectManager.PlaceLinkListener():

placeLinkListener = new ProxyObjectManager.PlaceLinkListener() {
    @Override
    public void onPlaceLinkLoaded(@NotNull PlaceLink placeLink) {
        String category = placeLink.getCategory();
        String name = placeLink.getName();
        PlacesManagerProvider.getInstance(new CoreInitCallback<PlacesManager>() {
        @Override
        public void onInstance(@NonNull PlacesManager placesManager) {
            placesManager.loadPlace(placeLink, new PlacesManager.PlaceListener() {
            @Override
            public void onPlaceLoaded(@NotNull Place place) {
                List<PlaceDetail> placeDetails = place.getDetails();
            }
            @Override
            public void onPlaceError(@NotNull PlacesManager.ErrorCode errorCode) {
            }
        });
        }
        @Override
        public void onError(@NotNull CoreInitException e) {
        }
        });
    }
    @Override
    public void onPlaceLinkError(@NotNull PlacesManager.ErrorCode errorCode) {
    }
};

Offline Maps

Applications developed with the Sygic Android SDK have the ability to pre-load map data, allowing the use of maps, search, routing, and other features without an active data connection. Let's dive right in.
We will work with an instance of MapLoader.
You can get the MapLoader just like any other instance - via the MapLoaderProvider.

The available maps are now requested using one call, only (no more Regions → Countries). The first parameter signifies whether you want to get the maps that are installed in your device already or the ones that are available online. false - online request, true - downloaded maps. The response is cached after the first online request and the next requests are therefore almost instantaneous.
If you would like to get the CountryDetails (such as continentName - using which you can sort the countries into continents, name, regions - if it's a country split map, it will have regions which can be downloaded separately according to their iso codes, totalSize), the usage is shown in the brief sample below. Again, you can choose whether you would like to explore an online or an already downloaded country.
Remember, that each map needs to be loaded after being downloaded and at each launch of the application.
Please always use the ISO codes that you get from the MapLoader as it's not guaranteed that the MapLoader always operates with the ISO 3166 alpha-2 standard

MapLoader mMapLoader;
MapLoaderProvider.getInstance(new CoreInitCallback<MapLoader>() {
    @Override
    public void onInstance(@NonNull MapLoader mapLoader) {
      mMapLoader = mapLoader;
    }

    @Override
    public void onError(@NonNull CoreInitException e) {
    }
});

MapResultListener mapResultListener = new MapResultListener() {
    @Override
    public void onMapResult(@NotNull String s, @NotNull MapLoader.LoadResult loadResult) {
        //load the map (mMapLoader.loadMap(s))
    }
};

mMapLoader.getAvailableCountries(false, new MapListResultListener() {
    @Override
    public void onMapListResult(@NotNull List<String> list, @NotNull MapLoader.LoadResult loadResult) {
        if (loadResult.equals(MapLoader.LoadResult.Success)) {
            mMapLoader.getCountryDetails(list.get(0), false, new MapCountryDetailsListener() {
            @Override
            public void onCountryDetails(@NotNull CountryDetails countryDetails) {
                mCountryDetails = countryDetails;
            }
            @Override
            public void onCountryDetailsError(@NotNull MapLoader.LoadResult loadResult) {
            }
        });
        mMapLoader.installMap(list.get(0), mapResultListener);
        }
        else {
            //error
        }
    }
});

Since the version 17, there's a new functionality - you can now detect a country of where you're located using your IP address and get its iso code by passing an empty string. If you pass it a region iso code, you will get the iso code of the country.

mMapLoader.detectCurrentCountry("", new MapResultListener() {
    @Override
    public void onMapResult(@NotNull String s, @NotNull MapLoader.LoadResult loadResult) {
        //here you can just call the mMapLoader.installMap(s) to install it directly
    }
});

You can add an install progress listener or resume the pending installations like this:

mMapLoader.addMapProgressInstallListener(new MapInstallProgressListener() {
    @Override
    public void onMapInstallProgress(String s, long l, long l1) {
    }
});
mMapLoader.resumePendingInstallations(new MapLoaderResumeInfoListener() {
    @Override
    public void onInfoProvided(@NotNull List<ResumedMapLoaderOperation> list, @NotNull List<ResumedMapLoaderOperation> list1, @NotNull MapLoader.LoadResult loadResult) {
    }
});

Do not forget to remove the listener once you don't need it anymore.
How to cancel a download? The installMap operation is a MapLoader's task, therefore:

MapLoader.Task task = mMapLoader.installMap("iso", mapResultListener);
task.cancel();

How to check for updates?

mMapLoader.checkForUpdates(new MapListResultListener() {
    @Override
    public void onMapListResult(@NotNull List<String> list, @NotNull MapLoader.LoadResult loadResult) {
        //install the maps that are available for update
    }
});