diff --git a/MapboxAndroidDemo/src/main/java/com/mapbox/mapboxandroiddemo/examples/javaservices/MatrixApiActivity.java b/MapboxAndroidDemo/src/main/java/com/mapbox/mapboxandroiddemo/examples/javaservices/MatrixApiActivity.java index 39321cfd3..31ac21a74 100644 --- a/MapboxAndroidDemo/src/main/java/com/mapbox/mapboxandroiddemo/examples/javaservices/MatrixApiActivity.java +++ b/MapboxAndroidDemo/src/main/java/com/mapbox/mapboxandroiddemo/examples/javaservices/MatrixApiActivity.java @@ -1,15 +1,8 @@ package com.mapbox.mapboxandroiddemo.examples.javaservices; import android.content.Context; +import android.graphics.BitmapFactory; import android.os.Bundle; -import androidx.annotation.NonNull; -import androidx.appcompat.app.AppCompatActivity; -import androidx.cardview.widget.CardView; -import androidx.recyclerview.widget.DefaultItemAnimator; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.LinearSnapHelper; -import androidx.recyclerview.widget.RecyclerView; -import androidx.recyclerview.widget.SnapHelper; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -24,15 +17,13 @@ import com.mapbox.geojson.Point; import com.mapbox.mapboxandroiddemo.R; import com.mapbox.mapboxsdk.Mapbox; -import com.mapbox.mapboxsdk.annotations.Icon; -import com.mapbox.mapboxsdk.annotations.IconFactory; -import com.mapbox.mapboxsdk.annotations.Marker; -import com.mapbox.mapboxsdk.annotations.MarkerOptions; import com.mapbox.mapboxsdk.geometry.LatLng; import com.mapbox.mapboxsdk.maps.MapView; import com.mapbox.mapboxsdk.maps.MapboxMap; import com.mapbox.mapboxsdk.maps.OnMapReadyCallback; import com.mapbox.mapboxsdk.maps.Style; +import com.mapbox.mapboxsdk.style.layers.SymbolLayer; +import com.mapbox.mapboxsdk.style.sources.GeoJsonSource; import com.mapbox.turf.TurfConversion; import java.io.InputStream; @@ -40,24 +31,38 @@ import java.util.ArrayList; import java.util.List; +import androidx.annotation.NonNull; +import androidx.appcompat.app.AppCompatActivity; +import androidx.cardview.widget.CardView; +import androidx.recyclerview.widget.DefaultItemAnimator; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.LinearSnapHelper; +import androidx.recyclerview.widget.RecyclerView; +import androidx.recyclerview.widget.SnapHelper; import retrofit2.Call; import retrofit2.Callback; import retrofit2.Response; import timber.log.Timber; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconAllowOverlap; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconIgnorePlacement; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconImage; /** - * Use the Mapbox Java Services SDK's Matrix API to retrieve travel times between many points. + * Use the Mapbox Java SDK's Matrix API to retrieve travel times between many points. */ -public class MatrixApiActivity extends AppCompatActivity { +public class MatrixApiActivity extends AppCompatActivity implements MapboxMap.OnMapClickListener { + private static final String ICON_ID = "ICON_ID"; + private static final String STATION_NAME_PROPERTY = "Station_Name"; + private static final String SOURCE_ID = "SOURCE_ID"; + private static final String LAYER_ID = "LAYER_ID"; + private List pointList; + private List matrixLocationList; private MapView mapView; private MapboxMap mapboxMap; - private List pointList; private FeatureCollection featureCollection; - private RecyclerView recyclerView; private MatrixApiLocationRecyclerViewAdapter matrixApiLocationRecyclerViewAdapter; - private ArrayList matrixLocationList; @Override protected void onCreate(Bundle savedInstanceState) { @@ -70,10 +75,8 @@ protected void onCreate(Bundle savedInstanceState) { // This contains the MapView in XML and needs to be called after the access token is configured. setContentView(R.layout.activity_matrix_api); - recyclerView = findViewById(R.id.matrix_api_recyclerview); - - // Create list of positions from local GeoJSON file - initPositionListFromGeoJsonFile(); + // Create a FeatureCollection via local GeoJSON file + initFeatureCollection(); mapView = findViewById(R.id.mapView); mapView.onCreate(savedInstanceState); @@ -81,13 +84,24 @@ protected void onCreate(Bundle savedInstanceState) { @Override public void onMapReady(@NonNull final MapboxMap mapboxMap) { MatrixApiActivity.this.mapboxMap = mapboxMap; - - mapboxMap.setStyle(new Style.Builder().fromUri("mapbox://styles/mapbox/cj8gg22et19ot2rnz65958fkn"), + mapboxMap.setStyle(new Style.Builder().fromUri("mapbox://styles/mapbox/cj8gg22et19ot2rnz65958fkn") + // Add the SymbolLayer icon image to the map style + .withImage(ICON_ID, BitmapFactory.decodeResource( + MatrixApiActivity.this.getResources(), R.drawable.lightning_bolt)) + + // Adding a GeoJson source for the SymbolLayer icons. + .withSource(new GeoJsonSource(SOURCE_ID, featureCollection)) + + // Adding the actual SymbolLayer to the map style. + .withLayer(new SymbolLayer(LAYER_ID, SOURCE_ID) + .withProperties( + iconImage(ICON_ID), + iconAllowOverlap(true), + iconIgnorePlacement(true)) + ), new Style.OnStyleLoaded() { @Override public void onStyleLoaded(@NonNull Style style) { - // Add markers to the map - addMarkers(); // Set up list of locations to pass to the recyclerview initMatrixLocationListForRecyclerView(); @@ -95,16 +109,7 @@ public void onStyleLoaded(@NonNull Style style) { // Set up the recyclerview of charging station cards initRecyclerView(); - mapboxMap.setOnMarkerClickListener(new MapboxMap.OnMarkerClickListener() { - @Override - public boolean onMarkerClick(@NonNull Marker marker) { - - // Make a call to the Mapbox Matrix API - makeMapboxMatrixApiCall(getClickedMarkerNumInPositionList(marker), Point.fromLngLat( - marker.getPosition().getLongitude(), marker.getPosition().getLatitude())); - return false; - } - }); + mapboxMap.addOnMapClickListener(MatrixApiActivity.this); Toast.makeText(MatrixApiActivity.this, R.string.click_on_marker_instruction_toast, Toast.LENGTH_SHORT).show(); } @@ -113,23 +118,32 @@ public boolean onMarkerClick(@NonNull Marker marker) { }); } - private int getClickedMarkerNumInPositionList(Marker clickedMarker) { - int clickedMarkerIndexPositionInList = -1; - if (clickedMarker != null) { - for (Marker singleMarker : mapboxMap.getMarkers()) { - if (singleMarker == clickedMarker) { - clickedMarkerIndexPositionInList = mapboxMap.getMarkers().indexOf(singleMarker); + @Override + public boolean onMapClick(@NonNull LatLng point) { + List renderedStationFeatures = mapboxMap.queryRenderedFeatures( + mapboxMap.getProjection().toScreenLocation(point), LAYER_ID); + if (!renderedStationFeatures.isEmpty()) { + Point pointOfSelectedStation = (Point) renderedStationFeatures.get(0).geometry(); + if (pointOfSelectedStation != null) { + String selectedBoltFeatureName = renderedStationFeatures.get(0).getStringProperty(STATION_NAME_PROPERTY); + List featureList = featureCollection.features(); + for (int i = 0; i < featureList.size(); i++) { + if (featureList.get(i).getStringProperty(STATION_NAME_PROPERTY).equals(selectedBoltFeatureName)) { + makeMapboxMatrixApiCall(i); + } } } - return clickedMarkerIndexPositionInList; - } else { - return 0; } + return true; } + /** + * Set up the RecyclerView, which will display the travel distances to each charge station. + */ private void initRecyclerView() { matrixApiLocationRecyclerViewAdapter = new MatrixApiLocationRecyclerViewAdapter(this, matrixLocationList); + RecyclerView recyclerView = findViewById(R.id.matrix_api_recyclerview); recyclerView.setLayoutManager(new LinearLayoutManager(getApplicationContext(), LinearLayoutManager.HORIZONTAL, true)); recyclerView.setItemAnimator(new DefaultItemAnimator()); @@ -138,8 +152,12 @@ private void initRecyclerView() { snapHelper.attachToRecyclerView(recyclerView); } - private void makeMapboxMatrixApiCall(final int markerPositionInList, Point pointOfClickedMarker) { - + /** + * Make a call to the Mapbox Matrix API to get the travel distances to each charge station. + * + * @param markerPositionInList the position of the tapped bolt icon {@link Feature} in the FeatureCollection. + */ + private void makeMapboxMatrixApiCall(final int markerPositionInList) { // Build Mapbox Matrix API parameters MapboxMatrix directionsMatrixClient = MapboxMatrix.builder() .accessToken(getString(R.string.access_token)) @@ -152,18 +170,19 @@ private void makeMapboxMatrixApiCall(final int markerPositionInList, Point point @Override public void onResponse(Call call, Response response) { - List durationsToAllOfTheLocationsFromTheOrigin = response.body().durations(); - for (int x = 0; x < durationsToAllOfTheLocationsFromTheOrigin.size(); x++) { - String finalConvertedFormattedDistance = String.valueOf(new DecimalFormat("#.##") - .format(TurfConversion.convertLength( - durationsToAllOfTheLocationsFromTheOrigin.get(markerPositionInList)[x], - "meters", "miles"))); - if (x == markerPositionInList) { - matrixLocationList.get(x).setDistanceFromOrigin(finalConvertedFormattedDistance); - } - if (x != markerPositionInList) { - matrixLocationList.get(x).setDistanceFromOrigin(finalConvertedFormattedDistance); - matrixApiLocationRecyclerViewAdapter.notifyDataSetChanged(); + if (response.body() != null) { + List durationsToAllOfTheLocationsFromTheOrigin = response.body().durations(); + if (durationsToAllOfTheLocationsFromTheOrigin != null) { + for (int x = 0; x < durationsToAllOfTheLocationsFromTheOrigin.size(); x++) { + String finalConvertedFormattedDistance = String.valueOf(new DecimalFormat("#.##").format( + TurfConversion.convertLength( + durationsToAllOfTheLocationsFromTheOrigin.get(markerPositionInList)[x], + "meters", "miles"))); + matrixLocationList.get(x).setDistanceFromOrigin(finalConvertedFormattedDistance); + if (x != markerPositionInList) { + matrixApiLocationRecyclerViewAdapter.notifyDataSetChanged(); + } + } } } } @@ -172,23 +191,11 @@ public void onResponse(Call call, public void onFailure(Call call, Throwable throwable) { Toast.makeText(MatrixApiActivity.this, R.string.call_error, Toast.LENGTH_SHORT).show(); - Timber.d( "onResponse onFailure"); + Timber.d("onResponse onFailure"); } }); } - private void addMarkers() { - Icon lightningBoltIcon = IconFactory.getInstance(MatrixApiActivity.this) - .fromResource(R.drawable.lightning_bolt); - for (Feature feature : featureCollection.features()) { - mapboxMap.addMarker(new MarkerOptions() - .position(new LatLng(feature.getProperty("Latitude").getAsDouble(), - feature.getProperty("Longitude").getAsDouble())) - .snippet(feature.getStringProperty("Station_Name")) - .icon(lightningBoltIcon)); - } - } - private String loadGeoJsonFromAsset(String filename) { try { // Load GeoJSON file from local asset folder @@ -205,8 +212,10 @@ private String loadGeoJsonFromAsset(String filename) { } } - private void initPositionListFromGeoJsonFile() { - + /** + * Create a {@link FeatureCollection} from a locally stored asset file. + */ + private void initFeatureCollection() { // Get GeoJSON features from GeoJSON file in the assets folder featureCollection = FeatureCollection.fromJson(loadGeoJsonFromAsset("boston_charge_stations.geojson")); @@ -215,20 +224,24 @@ private void initPositionListFromGeoJsonFile() { // Get the position of each GeoJSON feature and build the list of Position // objects for eventual use in the Matrix API call - for (Feature singleLocation : featureCollection.features()) { - pointList.add((Point) singleLocation.geometry()); + if (featureCollection != null && featureCollection.features() != null) { + for (Feature singleLocation : featureCollection.features()) { + pointList.add((Point) singleLocation.geometry()); + } } } + /** + * Create a list of {@link SingleRecyclerViewMatrixLocation} objects to eventually use in the RecyclerView. + */ private void initMatrixLocationListForRecyclerView() { matrixLocationList = new ArrayList<>(); - for (Feature feature : featureCollection.features()) { - SingleRecyclerViewMatrixLocation singleRecyclerViewLocation = new SingleRecyclerViewMatrixLocation(); - singleRecyclerViewLocation.setName(feature.getStringProperty("Station_Name")); - singleRecyclerViewLocation.setLocationLatLng(new LatLng(((Point) - feature.geometry()).latitude(), - ((Point) feature.geometry()).longitude())); - matrixLocationList.add(singleRecyclerViewLocation); + if (featureCollection != null && featureCollection.features() != null) { + for (Feature feature : featureCollection.features()) { + SingleRecyclerViewMatrixLocation singleRecyclerViewLocation = new SingleRecyclerViewMatrixLocation(); + singleRecyclerViewLocation.setName(feature.getStringProperty(STATION_NAME_PROPERTY)); + matrixLocationList.add(singleRecyclerViewLocation); + } } } @@ -281,7 +294,6 @@ protected void onSaveInstanceState(Bundle outState) { class SingleRecyclerViewMatrixLocation { private String name; - private LatLng locationLatLng; private String distanceFromOrigin; public String getName() { @@ -299,12 +311,11 @@ public String getDistanceFromOrigin() { public void setDistanceFromOrigin(String distanceFromOrigin) { this.distanceFromOrigin = distanceFromOrigin; } - - public void setLocationLatLng(LatLng locationLatLng) { - this.locationLatLng = locationLatLng; - } } + /** + * The adapter for this example's RecyclerView. + */ static class MatrixApiLocationRecyclerViewAdapter extends RecyclerView.Adapter { diff --git a/MapboxAndroidDemo/src/main/res/layout/activity_matrix_api.xml b/MapboxAndroidDemo/src/main/res/layout/activity_matrix_api.xml index 40a745834..6968634a4 100644 --- a/MapboxAndroidDemo/src/main/res/layout/activity_matrix_api.xml +++ b/MapboxAndroidDemo/src/main/res/layout/activity_matrix_api.xml @@ -11,6 +11,7 @@ mapbox:mapbox_cameraTargetLat="42.3600825" mapbox:mapbox_cameraTargetLng="-71.0588801" mapbox:mapbox_cameraZoom="11.193" + mapbox:mapbox_uiCompassMarginTop="79dp" /> Oh no! The call to the Directions Matrix API failed %1$s miles - Click on a bolt marker to get started + Click on a bolt icon to get started latitude