1
1
package com .mapbox .mapboxandroiddemo .examples .javaservices ;
2
2
3
3
import android .content .Context ;
4
+ import android .graphics .BitmapFactory ;
4
5
import 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 ;
13
6
import android .view .LayoutInflater ;
14
7
import android .view .View ;
15
8
import android .view .ViewGroup ;
24
17
import com .mapbox .geojson .Point ;
25
18
import com .mapbox .mapboxandroiddemo .R ;
26
19
import 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 ;
31
20
import com .mapbox .mapboxsdk .geometry .LatLng ;
32
21
import com .mapbox .mapboxsdk .maps .MapView ;
33
22
import com .mapbox .mapboxsdk .maps .MapboxMap ;
34
23
import com .mapbox .mapboxsdk .maps .OnMapReadyCallback ;
35
24
import com .mapbox .mapboxsdk .maps .Style ;
25
+ import com .mapbox .mapboxsdk .style .layers .SymbolLayer ;
26
+ import com .mapbox .mapboxsdk .style .sources .GeoJsonSource ;
36
27
import com .mapbox .turf .TurfConversion ;
37
28
38
29
import java .io .InputStream ;
39
30
import java .text .DecimalFormat ;
40
31
import java .util .ArrayList ;
41
32
import java .util .List ;
42
33
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 ;
43
42
import retrofit2 .Call ;
44
43
import retrofit2 .Callback ;
45
44
import retrofit2 .Response ;
46
45
import timber .log .Timber ;
47
46
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 ;
48
50
49
51
/**
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.
51
53
*/
52
- public class MatrixApiActivity extends AppCompatActivity {
54
+ public class MatrixApiActivity extends AppCompatActivity implements MapboxMap . OnMapClickListener {
53
55
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 ;
54
62
private MapView mapView ;
55
63
private MapboxMap mapboxMap ;
56
- private List <Point > pointList ;
57
64
private FeatureCollection featureCollection ;
58
- private RecyclerView recyclerView ;
59
65
private MatrixApiLocationRecyclerViewAdapter matrixApiLocationRecyclerViewAdapter ;
60
- private ArrayList <SingleRecyclerViewMatrixLocation > matrixLocationList ;
61
66
62
67
@ Override
63
68
protected void onCreate (Bundle savedInstanceState ) {
@@ -70,41 +75,41 @@ protected void onCreate(Bundle savedInstanceState) {
70
75
// This contains the MapView in XML and needs to be called after the access token is configured.
71
76
setContentView (R .layout .activity_matrix_api );
72
77
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 ();
77
80
78
81
mapView = findViewById (R .id .mapView );
79
82
mapView .onCreate (savedInstanceState );
80
83
mapView .getMapAsync (new OnMapReadyCallback () {
81
84
@ Override
82
85
public void onMapReady (@ NonNull final MapboxMap mapboxMap ) {
83
86
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
+ ),
86
102
new Style .OnStyleLoaded () {
87
103
@ Override
88
104
public void onStyleLoaded (@ NonNull Style style ) {
89
- // Add markers to the map
90
- addMarkers ();
91
105
92
106
// Set up list of locations to pass to the recyclerview
93
107
initMatrixLocationListForRecyclerView ();
94
108
95
109
// Set up the recyclerview of charging station cards
96
110
initRecyclerView ();
97
111
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 );
108
113
Toast .makeText (MatrixApiActivity .this , R .string .click_on_marker_instruction_toast ,
109
114
Toast .LENGTH_SHORT ).show ();
110
115
}
@@ -113,23 +118,32 @@ public boolean onMarkerClick(@NonNull Marker marker) {
113
118
});
114
119
}
115
120
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
+ }
122
134
}
123
135
}
124
- return clickedMarkerIndexPositionInList ;
125
- } else {
126
- return 0 ;
127
136
}
137
+ return true ;
128
138
}
129
139
140
+ /**
141
+ * Set up the RecyclerView, which will display the travel distances to each charge station.
142
+ */
130
143
private void initRecyclerView () {
131
144
matrixApiLocationRecyclerViewAdapter = new MatrixApiLocationRecyclerViewAdapter (this ,
132
145
matrixLocationList );
146
+ RecyclerView recyclerView = findViewById (R .id .matrix_api_recyclerview );
133
147
recyclerView .setLayoutManager (new LinearLayoutManager (getApplicationContext (),
134
148
LinearLayoutManager .HORIZONTAL , true ));
135
149
recyclerView .setItemAnimator (new DefaultItemAnimator ());
@@ -138,8 +152,12 @@ private void initRecyclerView() {
138
152
snapHelper .attachToRecyclerView (recyclerView );
139
153
}
140
154
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 ) {
143
161
// Build Mapbox Matrix API parameters
144
162
MapboxMatrix directionsMatrixClient = MapboxMatrix .builder ()
145
163
.accessToken (getString (R .string .access_token ))
@@ -152,18 +170,19 @@ private void makeMapboxMatrixApiCall(final int markerPositionInList, Point point
152
170
@ Override
153
171
public void onResponse (Call <MatrixResponse > call ,
154
172
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
+ }
167
186
}
168
187
}
169
188
}
@@ -172,23 +191,11 @@ public void onResponse(Call<MatrixResponse> call,
172
191
public void onFailure (Call <MatrixResponse > call , Throwable throwable ) {
173
192
Toast .makeText (MatrixApiActivity .this , R .string .call_error ,
174
193
Toast .LENGTH_SHORT ).show ();
175
- Timber .d ( "onResponse onFailure" );
194
+ Timber .d ("onResponse onFailure" );
176
195
}
177
196
});
178
197
}
179
198
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
-
192
199
private String loadGeoJsonFromAsset (String filename ) {
193
200
try {
194
201
// Load GeoJSON file from local asset folder
@@ -205,8 +212,10 @@ private String loadGeoJsonFromAsset(String filename) {
205
212
}
206
213
}
207
214
208
- private void initPositionListFromGeoJsonFile () {
209
-
215
+ /**
216
+ * Create a {@link FeatureCollection} from a locally stored asset file.
217
+ */
218
+ private void initFeatureCollection () {
210
219
// Get GeoJSON features from GeoJSON file in the assets folder
211
220
featureCollection = FeatureCollection .fromJson (loadGeoJsonFromAsset ("boston_charge_stations.geojson" ));
212
221
@@ -215,20 +224,24 @@ private void initPositionListFromGeoJsonFile() {
215
224
216
225
// Get the position of each GeoJSON feature and build the list of Position
217
226
// 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
+ }
220
231
}
221
232
}
222
233
234
+ /**
235
+ * Create a list of {@link SingleRecyclerViewMatrixLocation} objects to eventually use in the RecyclerView.
236
+ */
223
237
private void initMatrixLocationListForRecyclerView () {
224
238
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
+ }
232
245
}
233
246
}
234
247
@@ -281,7 +294,6 @@ protected void onSaveInstanceState(Bundle outState) {
281
294
class SingleRecyclerViewMatrixLocation {
282
295
283
296
private String name ;
284
- private LatLng locationLatLng ;
285
297
private String distanceFromOrigin ;
286
298
287
299
public String getName () {
@@ -299,12 +311,11 @@ public String getDistanceFromOrigin() {
299
311
public void setDistanceFromOrigin (String distanceFromOrigin ) {
300
312
this .distanceFromOrigin = distanceFromOrigin ;
301
313
}
302
-
303
- public void setLocationLatLng (LatLng locationLatLng ) {
304
- this .locationLatLng = locationLatLng ;
305
- }
306
314
}
307
315
316
+ /**
317
+ * The adapter for this example's RecyclerView.
318
+ */
308
319
static class MatrixApiLocationRecyclerViewAdapter extends
309
320
RecyclerView .Adapter <MatrixApiLocationRecyclerViewAdapter .MyViewHolder > {
310
321
0 commit comments