11package com .mapbox .mapboxandroiddemo .examples .javaservices ;
22
33import android .content .Context ;
4+ import android .graphics .BitmapFactory ;
45import android .os .Bundle ;
5- import androidx .annotation .NonNull ;
6- import androidx .appcompat .app .AppCompatActivity ;
7- import androidx .cardview .widget .CardView ;
8- import androidx .recyclerview .widget .DefaultItemAnimator ;
9- import androidx .recyclerview .widget .LinearLayoutManager ;
10- import androidx .recyclerview .widget .LinearSnapHelper ;
11- import androidx .recyclerview .widget .RecyclerView ;
12- import androidx .recyclerview .widget .SnapHelper ;
136import android .view .LayoutInflater ;
147import android .view .View ;
158import android .view .ViewGroup ;
2417import com .mapbox .geojson .Point ;
2518import com .mapbox .mapboxandroiddemo .R ;
2619import com .mapbox .mapboxsdk .Mapbox ;
27- import com .mapbox .mapboxsdk .annotations .Icon ;
28- import com .mapbox .mapboxsdk .annotations .IconFactory ;
29- import com .mapbox .mapboxsdk .annotations .Marker ;
30- import com .mapbox .mapboxsdk .annotations .MarkerOptions ;
3120import com .mapbox .mapboxsdk .geometry .LatLng ;
3221import com .mapbox .mapboxsdk .maps .MapView ;
3322import com .mapbox .mapboxsdk .maps .MapboxMap ;
3423import com .mapbox .mapboxsdk .maps .OnMapReadyCallback ;
3524import com .mapbox .mapboxsdk .maps .Style ;
25+ import com .mapbox .mapboxsdk .style .layers .SymbolLayer ;
26+ import com .mapbox .mapboxsdk .style .sources .GeoJsonSource ;
3627import com .mapbox .turf .TurfConversion ;
3728
3829import java .io .InputStream ;
3930import java .text .DecimalFormat ;
4031import java .util .ArrayList ;
4132import java .util .List ;
4233
34+ import androidx .annotation .NonNull ;
35+ import androidx .appcompat .app .AppCompatActivity ;
36+ import androidx .cardview .widget .CardView ;
37+ import androidx .recyclerview .widget .DefaultItemAnimator ;
38+ import androidx .recyclerview .widget .LinearLayoutManager ;
39+ import androidx .recyclerview .widget .LinearSnapHelper ;
40+ import androidx .recyclerview .widget .RecyclerView ;
41+ import androidx .recyclerview .widget .SnapHelper ;
4342import retrofit2 .Call ;
4443import retrofit2 .Callback ;
4544import retrofit2 .Response ;
4645import timber .log .Timber ;
4746
47+ import static com .mapbox .mapboxsdk .style .layers .PropertyFactory .iconAllowOverlap ;
48+ import static com .mapbox .mapboxsdk .style .layers .PropertyFactory .iconIgnorePlacement ;
49+ import static com .mapbox .mapboxsdk .style .layers .PropertyFactory .iconImage ;
4850
4951/**
50- * Use the Mapbox Java Services SDK's Matrix API to retrieve travel times between many points.
52+ * Use the Mapbox Java SDK's Matrix API to retrieve travel times between many points.
5153 */
52- public class MatrixApiActivity extends AppCompatActivity {
54+ public class MatrixApiActivity extends AppCompatActivity implements MapboxMap . OnMapClickListener {
5355
56+ private static final String ICON_ID = "ICON_ID" ;
57+ private static final String STATION_NAME_PROPERTY = "Station_Name" ;
58+ private static final String SOURCE_ID = "SOURCE_ID" ;
59+ private static final String LAYER_ID = "LAYER_ID" ;
60+ private List <Point > pointList ;
61+ private List <SingleRecyclerViewMatrixLocation > matrixLocationList ;
5462 private MapView mapView ;
5563 private MapboxMap mapboxMap ;
56- private List <Point > pointList ;
5764 private FeatureCollection featureCollection ;
58- private RecyclerView recyclerView ;
5965 private MatrixApiLocationRecyclerViewAdapter matrixApiLocationRecyclerViewAdapter ;
60- private ArrayList <SingleRecyclerViewMatrixLocation > matrixLocationList ;
6166
6267 @ Override
6368 protected void onCreate (Bundle savedInstanceState ) {
@@ -70,41 +75,41 @@ protected void onCreate(Bundle savedInstanceState) {
7075 // This contains the MapView in XML and needs to be called after the access token is configured.
7176 setContentView (R .layout .activity_matrix_api );
7277
73- recyclerView = findViewById (R .id .matrix_api_recyclerview );
74-
75- // Create list of positions from local GeoJSON file
76- initPositionListFromGeoJsonFile ();
78+ // Create a FeatureCollection via local GeoJSON file
79+ initFeatureCollection ();
7780
7881 mapView = findViewById (R .id .mapView );
7982 mapView .onCreate (savedInstanceState );
8083 mapView .getMapAsync (new OnMapReadyCallback () {
8184 @ Override
8285 public void onMapReady (@ NonNull final MapboxMap mapboxMap ) {
8386 MatrixApiActivity .this .mapboxMap = mapboxMap ;
84-
85- mapboxMap .setStyle (new Style .Builder ().fromUri ("mapbox://styles/mapbox/cj8gg22et19ot2rnz65958fkn" ),
87+ mapboxMap .setStyle (new Style .Builder ().fromUri ("mapbox://styles/mapbox/cj8gg22et19ot2rnz65958fkn" )
88+ // Add the SymbolLayer icon image to the map style
89+ .withImage (ICON_ID , BitmapFactory .decodeResource (
90+ MatrixApiActivity .this .getResources (), R .drawable .lightning_bolt ))
91+
92+ // Adding a GeoJson source for the SymbolLayer icons.
93+ .withSource (new GeoJsonSource (SOURCE_ID , featureCollection ))
94+
95+ // Adding the actual SymbolLayer to the map style.
96+ .withLayer (new SymbolLayer (LAYER_ID , SOURCE_ID )
97+ .withProperties (
98+ iconImage (ICON_ID ),
99+ iconAllowOverlap (true ),
100+ iconIgnorePlacement (true ))
101+ ),
86102 new Style .OnStyleLoaded () {
87103 @ Override
88104 public void onStyleLoaded (@ NonNull Style style ) {
89- // Add markers to the map
90- addMarkers ();
91105
92106 // Set up list of locations to pass to the recyclerview
93107 initMatrixLocationListForRecyclerView ();
94108
95109 // Set up the recyclerview of charging station cards
96110 initRecyclerView ();
97111
98- mapboxMap .setOnMarkerClickListener (new MapboxMap .OnMarkerClickListener () {
99- @ Override
100- public boolean onMarkerClick (@ NonNull Marker marker ) {
101-
102- // Make a call to the Mapbox Matrix API
103- makeMapboxMatrixApiCall (getClickedMarkerNumInPositionList (marker ), Point .fromLngLat (
104- marker .getPosition ().getLongitude (), marker .getPosition ().getLatitude ()));
105- return false ;
106- }
107- });
112+ mapboxMap .addOnMapClickListener (MatrixApiActivity .this );
108113 Toast .makeText (MatrixApiActivity .this , R .string .click_on_marker_instruction_toast ,
109114 Toast .LENGTH_SHORT ).show ();
110115 }
@@ -113,23 +118,32 @@ public boolean onMarkerClick(@NonNull Marker marker) {
113118 });
114119 }
115120
116- private int getClickedMarkerNumInPositionList (Marker clickedMarker ) {
117- int clickedMarkerIndexPositionInList = -1 ;
118- if (clickedMarker != null ) {
119- for (Marker singleMarker : mapboxMap .getMarkers ()) {
120- if (singleMarker == clickedMarker ) {
121- clickedMarkerIndexPositionInList = mapboxMap .getMarkers ().indexOf (singleMarker );
121+ @ Override
122+ public boolean onMapClick (@ NonNull LatLng point ) {
123+ List <Feature > renderedStationFeatures = mapboxMap .queryRenderedFeatures (
124+ mapboxMap .getProjection ().toScreenLocation (point ), LAYER_ID );
125+ if (!renderedStationFeatures .isEmpty ()) {
126+ Point pointOfSelectedStation = (Point ) renderedStationFeatures .get (0 ).geometry ();
127+ if (pointOfSelectedStation != null ) {
128+ String selectedBoltFeatureName = renderedStationFeatures .get (0 ).getStringProperty (STATION_NAME_PROPERTY );
129+ List <Feature > featureList = featureCollection .features ();
130+ for (int i = 0 ; i < featureList .size (); i ++) {
131+ if (featureList .get (i ).getStringProperty (STATION_NAME_PROPERTY ).equals (selectedBoltFeatureName )) {
132+ makeMapboxMatrixApiCall (i );
133+ }
122134 }
123135 }
124- return clickedMarkerIndexPositionInList ;
125- } else {
126- return 0 ;
127136 }
137+ return true ;
128138 }
129139
140+ /**
141+ * Set up the RecyclerView, which will display the travel distances to each charge station.
142+ */
130143 private void initRecyclerView () {
131144 matrixApiLocationRecyclerViewAdapter = new MatrixApiLocationRecyclerViewAdapter (this ,
132145 matrixLocationList );
146+ RecyclerView recyclerView = findViewById (R .id .matrix_api_recyclerview );
133147 recyclerView .setLayoutManager (new LinearLayoutManager (getApplicationContext (),
134148 LinearLayoutManager .HORIZONTAL , true ));
135149 recyclerView .setItemAnimator (new DefaultItemAnimator ());
@@ -138,8 +152,12 @@ private void initRecyclerView() {
138152 snapHelper .attachToRecyclerView (recyclerView );
139153 }
140154
141- private void makeMapboxMatrixApiCall (final int markerPositionInList , Point pointOfClickedMarker ) {
142-
155+ /**
156+ * Make a call to the Mapbox Matrix API to get the travel distances to each charge station.
157+ *
158+ * @param markerPositionInList the position of the tapped bolt icon {@link Feature} in the FeatureCollection.
159+ */
160+ private void makeMapboxMatrixApiCall (final int markerPositionInList ) {
143161 // Build Mapbox Matrix API parameters
144162 MapboxMatrix directionsMatrixClient = MapboxMatrix .builder ()
145163 .accessToken (getString (R .string .access_token ))
@@ -152,18 +170,19 @@ private void makeMapboxMatrixApiCall(final int markerPositionInList, Point point
152170 @ Override
153171 public void onResponse (Call <MatrixResponse > call ,
154172 Response <MatrixResponse > response ) {
155- List <Double []> durationsToAllOfTheLocationsFromTheOrigin = response .body ().durations ();
156- for (int x = 0 ; x < durationsToAllOfTheLocationsFromTheOrigin .size (); x ++) {
157- String finalConvertedFormattedDistance = String .valueOf (new DecimalFormat ("#.##" )
158- .format (TurfConversion .convertLength (
159- durationsToAllOfTheLocationsFromTheOrigin .get (markerPositionInList )[x ],
160- "meters" , "miles" )));
161- if (x == markerPositionInList ) {
162- matrixLocationList .get (x ).setDistanceFromOrigin (finalConvertedFormattedDistance );
163- }
164- if (x != markerPositionInList ) {
165- matrixLocationList .get (x ).setDistanceFromOrigin (finalConvertedFormattedDistance );
166- matrixApiLocationRecyclerViewAdapter .notifyDataSetChanged ();
173+ if (response .body () != null ) {
174+ List <Double []> durationsToAllOfTheLocationsFromTheOrigin = response .body ().durations ();
175+ if (durationsToAllOfTheLocationsFromTheOrigin != null ) {
176+ for (int x = 0 ; x < durationsToAllOfTheLocationsFromTheOrigin .size (); x ++) {
177+ String finalConvertedFormattedDistance = String .valueOf (new DecimalFormat ("#.##" ).format (
178+ TurfConversion .convertLength (
179+ durationsToAllOfTheLocationsFromTheOrigin .get (markerPositionInList )[x ],
180+ "meters" , "miles" )));
181+ matrixLocationList .get (x ).setDistanceFromOrigin (finalConvertedFormattedDistance );
182+ if (x != markerPositionInList ) {
183+ matrixApiLocationRecyclerViewAdapter .notifyDataSetChanged ();
184+ }
185+ }
167186 }
168187 }
169188 }
@@ -172,23 +191,11 @@ public void onResponse(Call<MatrixResponse> call,
172191 public void onFailure (Call <MatrixResponse > call , Throwable throwable ) {
173192 Toast .makeText (MatrixApiActivity .this , R .string .call_error ,
174193 Toast .LENGTH_SHORT ).show ();
175- Timber .d ( "onResponse onFailure" );
194+ Timber .d ("onResponse onFailure" );
176195 }
177196 });
178197 }
179198
180- private void addMarkers () {
181- Icon lightningBoltIcon = IconFactory .getInstance (MatrixApiActivity .this )
182- .fromResource (R .drawable .lightning_bolt );
183- for (Feature feature : featureCollection .features ()) {
184- mapboxMap .addMarker (new MarkerOptions ()
185- .position (new LatLng (feature .getProperty ("Latitude" ).getAsDouble (),
186- feature .getProperty ("Longitude" ).getAsDouble ()))
187- .snippet (feature .getStringProperty ("Station_Name" ))
188- .icon (lightningBoltIcon ));
189- }
190- }
191-
192199 private String loadGeoJsonFromAsset (String filename ) {
193200 try {
194201 // Load GeoJSON file from local asset folder
@@ -205,8 +212,10 @@ private String loadGeoJsonFromAsset(String filename) {
205212 }
206213 }
207214
208- private void initPositionListFromGeoJsonFile () {
209-
215+ /**
216+ * Create a {@link FeatureCollection} from a locally stored asset file.
217+ */
218+ private void initFeatureCollection () {
210219 // Get GeoJSON features from GeoJSON file in the assets folder
211220 featureCollection = FeatureCollection .fromJson (loadGeoJsonFromAsset ("boston_charge_stations.geojson" ));
212221
@@ -215,20 +224,24 @@ private void initPositionListFromGeoJsonFile() {
215224
216225 // Get the position of each GeoJSON feature and build the list of Position
217226 // objects for eventual use in the Matrix API call
218- for (Feature singleLocation : featureCollection .features ()) {
219- pointList .add ((Point ) singleLocation .geometry ());
227+ if (featureCollection != null && featureCollection .features () != null ) {
228+ for (Feature singleLocation : featureCollection .features ()) {
229+ pointList .add ((Point ) singleLocation .geometry ());
230+ }
220231 }
221232 }
222233
234+ /**
235+ * Create a list of {@link SingleRecyclerViewMatrixLocation} objects to eventually use in the RecyclerView.
236+ */
223237 private void initMatrixLocationListForRecyclerView () {
224238 matrixLocationList = new ArrayList <>();
225- for (Feature feature : featureCollection .features ()) {
226- SingleRecyclerViewMatrixLocation singleRecyclerViewLocation = new SingleRecyclerViewMatrixLocation ();
227- singleRecyclerViewLocation .setName (feature .getStringProperty ("Station_Name" ));
228- singleRecyclerViewLocation .setLocationLatLng (new LatLng (((Point )
229- feature .geometry ()).latitude (),
230- ((Point ) feature .geometry ()).longitude ()));
231- matrixLocationList .add (singleRecyclerViewLocation );
239+ if (featureCollection != null && featureCollection .features () != null ) {
240+ for (Feature feature : featureCollection .features ()) {
241+ SingleRecyclerViewMatrixLocation singleRecyclerViewLocation = new SingleRecyclerViewMatrixLocation ();
242+ singleRecyclerViewLocation .setName (feature .getStringProperty (STATION_NAME_PROPERTY ));
243+ matrixLocationList .add (singleRecyclerViewLocation );
244+ }
232245 }
233246 }
234247
@@ -281,7 +294,6 @@ protected void onSaveInstanceState(Bundle outState) {
281294 class SingleRecyclerViewMatrixLocation {
282295
283296 private String name ;
284- private LatLng locationLatLng ;
285297 private String distanceFromOrigin ;
286298
287299 public String getName () {
@@ -299,12 +311,11 @@ public String getDistanceFromOrigin() {
299311 public void setDistanceFromOrigin (String distanceFromOrigin ) {
300312 this .distanceFromOrigin = distanceFromOrigin ;
301313 }
302-
303- public void setLocationLatLng (LatLng locationLatLng ) {
304- this .locationLatLng = locationLatLng ;
305- }
306314 }
307315
316+ /**
317+ * The adapter for this example's RecyclerView.
318+ */
308319 static class MatrixApiLocationRecyclerViewAdapter extends
309320 RecyclerView .Adapter <MatrixApiLocationRecyclerViewAdapter .MyViewHolder > {
310321
0 commit comments