メインコンテンツまでスキップ

Drawing search area

A newer version of the Maps SDK is available
This page uses v9.7.1 of the Mapbox Maps SDK. A newer version of the SDK is available. Learn about the latest version, v11.9.0, in the Maps SDK documentation.
Note

This example is a part of the Mapbox Android Demo app. You can find the values for all referenced resources in the res directory. For example, see res/values/activity_strings.xml for R.string.* references used in this example. The dependencies can be found here.The examples use View binding.See setup documention if necessary.

activity_finger_drag_draw
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:mapbox="http://schemas.android.com/apk/res-auto"
xmlns:fab="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">

<com.mapbox.mapboxsdk.maps.MapView
android:id="@+id/mapView"
android:layout_width="match_parent"
android:layout_height="match_parent"
mapbox:mapbox_cameraTargetLat="35.087497"
mapbox:mapbox_cameraZoom="11.679132"
mapbox:mapbox_cameraTargetLng="-106.651261"/>


<com.getbase.floatingactionbutton.FloatingActionsMenu
android:id="@+id/multiple_actions_parent_fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|bottom"
android:layout_margin="16dp"
fab:fab_addButtonColorNormal="@color/mapboxRed"
fab:fab_addButtonColorPressed="@color/mapboxWhite"
fab:fab_addButtonPlusIconColor="@color/mapboxWhite"
fab:fab_labelStyle="@style/menu_labels_style">

<com.getbase.floatingactionbutton.FloatingActionButton
android:id="@+id/switch_to_single_line_only_fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
fab:fab_colorNormal="@color/mapboxOrange"
fab:fab_colorPressed="@color/mapboxWhite"
fab:fab_size="mini"
fab:fab_title="@string/toggle_straight_line_drawing"/>

<com.getbase.floatingactionbutton.FloatingActionButton
android:id="@+id/clear_map_for_new_draw_fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
fab:fab_colorNormal="@color/mapboxBlue"
fab:fab_colorPressed="@color/mapboxWhite"
fab:fab_size="mini"
fab:fab_title="@string/clear_map"/>

<com.getbase.floatingactionbutton.FloatingActionButton
android:id="@+id/show_search_data_points_fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
fab:fab_colorNormal="@color/mapboxGreen"
fab:fab_colorPressed="@color/mapboxWhite"
fab:fab_size="mini"
fab:fab_title="@string/toggle_search_data_points_title"/>


</com.getbase.floatingactionbutton.FloatingActionsMenu>

</FrameLayout>
FingerDrawQueryActivity.java
package com.mapbox.mapboxandroiddemo.examples.query;

import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.graphics.PointF;
import android.os.AsyncTask;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Toast;

import com.mapbox.geojson.Feature;
import com.mapbox.geojson.FeatureCollection;
import com.mapbox.geojson.LineString;
import com.mapbox.geojson.Point;
import com.mapbox.geojson.Polygon;
import com.mapbox.mapboxandroiddemo.R;
import com.mapbox.mapboxsdk.Mapbox;
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.FillLayer;
import com.mapbox.mapboxsdk.style.layers.Layer;
import com.mapbox.mapboxsdk.style.layers.LineLayer;
import com.mapbox.mapboxsdk.style.layers.SymbolLayer;
import com.mapbox.mapboxsdk.style.sources.GeoJsonSource;
import com.mapbox.turf.TurfJoins;

import java.io.InputStream;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

import timber.log.Timber;

import static com.mapbox.mapboxsdk.style.layers.Property.LINE_JOIN_ROUND;
import static com.mapbox.mapboxsdk.style.layers.Property.NONE;
import static com.mapbox.mapboxsdk.style.layers.Property.VISIBLE;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillColor;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillOpacity;
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;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconOffset;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.lineColor;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.lineJoin;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.lineOpacity;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.lineWidth;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.visibility;

/**
* Use the Android system {@link android.view.View.OnTouchListener} to draw
* an polygon and/or a line. Also perform a search for data points within the drawn polygon area.
*/
public class FingerDrawQueryActivity extends AppCompatActivity {

private static final String SEARCH_DATA_SYMBOL_LAYER_SOURCE_ID = "SEARCH_DATA_SYMBOL_LAYER_SOURCE_ID";
private static final String FREEHAND_DRAW_LINE_LAYER_SOURCE_ID = "FREEHAND_DRAW_LINE_LAYER_SOURCE_ID";
private static final String MARKER_SYMBOL_LAYER_SOURCE_ID = "MARKER_SYMBOL_LAYER_SOURCE_ID";
private static final String FREEHAND_DRAW_FILL_LAYER_SOURCE_ID = "FREEHAND_DRAW_FILL_LAYER_SOURCE_ID";
private static final String FREEHAND_DRAW_LINE_LAYER_ID = "FREEHAND_DRAW_LINE_LAYER_ID";
private static final String FREEHAND_DRAW_FILL_LAYER_ID = "FREEHAND_DRAW_FILL_LAYER_ID";
private static final String SEARCH_DATA_SYMBOL_LAYER_ID = "SEARCH_DATA_SYMBOL_LAYER_ID";
private static final String SEARCH_DATA_MARKER_ID = "SEARCH_DATA_MARKER_ID";
private static final String LINE_COLOR = "#a0861c";
private static final float LINE_WIDTH = 5f;
private static final float LINE_OPACITY = 1f;
private static final float FILL_OPACITY = .4f;

private MapView mapView;
private MapboxMap mapboxMap;
private FeatureCollection searchPointFeatureCollection;
private List<Point> freehandTouchPointListForPolygon = new ArrayList<>();
private List<Point> freehandTouchPointListForLine = new ArrayList<>();
private boolean showSearchDataLocations = true;
private boolean drawSingleLineOnly = false;

private View.OnTouchListener customOnTouchListener = new View.OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {

LatLng latLngTouchCoordinate = mapboxMap.getProjection().fromScreenLocation(
new PointF(motionEvent.getX(), motionEvent.getY()));

Point screenTouchPoint = Point.fromLngLat(latLngTouchCoordinate.getLongitude(),
latLngTouchCoordinate.getLatitude());

// Draw the line on the map as the finger is dragged along the map
freehandTouchPointListForLine.add(screenTouchPoint);
mapboxMap.getStyle(new Style.OnStyleLoaded() {
@Override
public void onStyleLoaded(@NonNull Style style) {
GeoJsonSource drawLineSource = style.getSourceAs(FREEHAND_DRAW_LINE_LAYER_SOURCE_ID);
if (drawLineSource != null) {
drawLineSource.setGeoJson(LineString.fromLngLats(freehandTouchPointListForLine));
}

// Draw a polygon area if drawSingleLineOnly == false
if (!drawSingleLineOnly) {
if (freehandTouchPointListForPolygon.size() < 2) {
freehandTouchPointListForPolygon.add(screenTouchPoint);
} else if (freehandTouchPointListForPolygon.size() == 2) {
freehandTouchPointListForPolygon.add(screenTouchPoint);
freehandTouchPointListForPolygon.add(freehandTouchPointListForPolygon.get(0));
} else {
freehandTouchPointListForPolygon.remove(freehandTouchPointListForPolygon.size() - 1);
freehandTouchPointListForPolygon.add(screenTouchPoint);
freehandTouchPointListForPolygon.add(freehandTouchPointListForPolygon.get(0));
}
}

// Create and show a FillLayer polygon where the search area is
GeoJsonSource fillPolygonSource = style.getSourceAs(FREEHAND_DRAW_FILL_LAYER_SOURCE_ID);
List<List<Point>> polygonList = new ArrayList<>();
polygonList.add(freehandTouchPointListForPolygon);
Polygon drawnPolygon = Polygon.fromLngLats(polygonList);
if (fillPolygonSource != null) {
fillPolygonSource.setGeoJson(drawnPolygon);
}

// Take certain actions when the drawing is done
if (motionEvent.getAction() == MotionEvent.ACTION_UP) {

// If drawing polygon, add the first screen touch point to the end of
// the LineLayer list so that it's
if (!drawSingleLineOnly) {
freehandTouchPointListForLine.add(freehandTouchPointListForPolygon.get(0));
}

if (showSearchDataLocations && !drawSingleLineOnly) {

// Use Turf to calculate the number of data points within a certain Polygon area
FeatureCollection pointsInSearchAreaFeatureCollection =
TurfJoins.pointsWithinPolygon(searchPointFeatureCollection,
FeatureCollection.fromFeature(Feature.fromGeometry(
drawnPolygon)));

// Create a Toast which say show many data points within a certain Polygon area
if (VISIBLE.equals(style.getLayer(
SEARCH_DATA_SYMBOL_LAYER_ID).getVisibility().getValue())) {
Toast.makeText(FingerDrawQueryActivity.this, String.format(
getString(R.string.search_result_size),
pointsInSearchAreaFeatureCollection.features().size()), Toast.LENGTH_SHORT).show();
}
}

if (drawSingleLineOnly) {
Toast.makeText(FingerDrawQueryActivity.this,
getString(R.string.move_map_drawn_line), Toast.LENGTH_SHORT).show();
}
enableMapMovement();
}
}
});

return true;
}
};

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

// Mapbox access token is configured here. This needs to be called either in your application
// object or in the same activity which contains the mapview.
Mapbox.getInstance(this, getString(R.string.access_token));

// This contains the MapView in XML and needs to be called after the access token is configured.
setContentView(R.layout.activity_finger_drag_draw);

mapView = findViewById(R.id.mapView);
mapView.onCreate(savedInstanceState);
mapView.getMapAsync(new OnMapReadyCallback() {
@Override
public void onMapReady(@NonNull final MapboxMap mapboxMap) {
mapboxMap.setStyle(Style.LIGHT, new Style.OnStyleLoaded() {
@Override
public void onStyleLoaded(@NonNull Style style) {

FingerDrawQueryActivity.this.mapboxMap = mapboxMap;

if (showSearchDataLocations) {
new LoadGeoJson(FingerDrawQueryActivity.this).execute();
} else {
setUpExample(null);
}

findViewById(R.id.clear_map_for_new_draw_fab)
.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {

// Reset ArrayLists
freehandTouchPointListForPolygon = new ArrayList<>();
freehandTouchPointListForLine = new ArrayList<>();

// Add empty Feature array to the sources
GeoJsonSource drawLineSource = style.getSourceAs(FREEHAND_DRAW_LINE_LAYER_SOURCE_ID);
if (drawLineSource != null) {
drawLineSource.setGeoJson(FeatureCollection.fromFeatures(new Feature[]{}));
}

GeoJsonSource fillPolygonSource = style.getSourceAs(FREEHAND_DRAW_FILL_LAYER_SOURCE_ID);
if (fillPolygonSource != null) {
fillPolygonSource.setGeoJson(FeatureCollection.fromFeatures(new Feature[]{}));
}

enableMapDrawing();
}
});

findViewById(R.id.switch_to_single_line_only_fab)
.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
drawSingleLineOnly = !drawSingleLineOnly;
Toast.makeText(FingerDrawQueryActivity.this, String.format(
getString(R.string.now_drawing), drawSingleLineOnly ? getString(R.string.single_line) :
getString(R.string.polygon)), Toast.LENGTH_SHORT).show();
}
});
}
});
}
});
}

/**
* Enable moving the map
*/
private void enableMapMovement() {
mapView.setOnTouchListener(null);
}

/**
* Enable drawing on the map by setting the custom touch listener on the {@link MapView}
*/
private void enableMapDrawing() {
mapView.setOnTouchListener(customOnTouchListener);
}

private void setUpExample(FeatureCollection searchDataFeatureCollection) {

searchPointFeatureCollection = searchDataFeatureCollection;

mapboxMap.getStyle(new Style.OnStyleLoaded() {
@Override
public void onStyleLoaded(@NonNull Style loadedStyle) {
loadedStyle.addImage(SEARCH_DATA_MARKER_ID, BitmapFactory.decodeResource(
FingerDrawQueryActivity.this.getResources(), R.drawable.blue_marker_view));

// Add sources to the map
loadedStyle.addSource(new GeoJsonSource(SEARCH_DATA_SYMBOL_LAYER_SOURCE_ID,
searchDataFeatureCollection));
loadedStyle.addSource(new GeoJsonSource(FREEHAND_DRAW_LINE_LAYER_SOURCE_ID));
loadedStyle.addSource(new GeoJsonSource(MARKER_SYMBOL_LAYER_SOURCE_ID));
loadedStyle.addSource(new GeoJsonSource(FREEHAND_DRAW_FILL_LAYER_SOURCE_ID));

loadedStyle.addLayer(new SymbolLayer(SEARCH_DATA_SYMBOL_LAYER_ID,
SEARCH_DATA_SYMBOL_LAYER_SOURCE_ID).withProperties(
iconImage(SEARCH_DATA_MARKER_ID),
iconAllowOverlap(true),
iconOffset(new Float[] {0f, -8f}),
iconIgnorePlacement(true))
);

// Add freehand draw LineLayer to the map
loadedStyle.addLayerBelow(new LineLayer(FREEHAND_DRAW_LINE_LAYER_ID,
FREEHAND_DRAW_LINE_LAYER_SOURCE_ID).withProperties(
lineWidth(LINE_WIDTH),
lineJoin(LINE_JOIN_ROUND),
lineOpacity(LINE_OPACITY),
lineColor(Color.parseColor(LINE_COLOR))), SEARCH_DATA_SYMBOL_LAYER_ID
);

// Add freehand draw polygon FillLayer to the map
loadedStyle.addLayerBelow(new FillLayer(FREEHAND_DRAW_FILL_LAYER_ID,
FREEHAND_DRAW_FILL_LAYER_SOURCE_ID).withProperties(
fillColor(Color.RED),
fillOpacity(FILL_OPACITY)), FREEHAND_DRAW_LINE_LAYER_ID
);

enableMapDrawing();

findViewById(R.id.show_search_data_points_fab).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
showSearchDataLocations = !showSearchDataLocations;

// Toggle the visibility of the fake data point SymbolLayer icons
Layer dataLayer = loadedStyle.getLayer(SEARCH_DATA_SYMBOL_LAYER_ID);
if (dataLayer != null) {
dataLayer.setProperties(
VISIBLE.equals(dataLayer.getVisibility().getValue()) ? visibility(NONE) : visibility(VISIBLE));
}
}
});

Toast.makeText(FingerDrawQueryActivity.this,
getString(R.string.draw_instruction), Toast.LENGTH_SHORT).show();
}
});
}

/**
* Use an AsyncTask to retrieve GeoJSON data from a file in the assets folder.
*/
private static class LoadGeoJson extends AsyncTask<Void, Void, FeatureCollection> {

private WeakReference<FingerDrawQueryActivity> weakReference;

LoadGeoJson(FingerDrawQueryActivity activity) {
this.weakReference = new WeakReference<>(activity);
}

@Override
protected FeatureCollection doInBackground(Void... voids) {
try {
FingerDrawQueryActivity activity = weakReference.get();
if (activity != null) {
InputStream inputStream = activity.getAssets().open("albuquerque_locations.geojson");
return FeatureCollection.fromJson(convertStreamToString(inputStream));
}
} catch (Exception exception) {
Timber.e("Exception Loading GeoJSON: %s", exception.toString());
}
return null;
}

static String convertStreamToString(InputStream is) {
Scanner scanner = new Scanner(is).useDelimiter("\\A");
return scanner.hasNext() ? scanner.next() : "";
}


@Override
protected void onPostExecute(@Nullable FeatureCollection featureCollection) {
super.onPostExecute(featureCollection);
FingerDrawQueryActivity activity = weakReference.get();
if (activity != null && featureCollection != null) {
activity.setUpExample(featureCollection);
}
}
}

// Add the mapView lifecycle to the activity's lifecycle methods
@Override
public void onResume() {
super.onResume();
mapView.onResume();
}

@Override
protected void onStart() {
super.onStart();
mapView.onStart();
}

@Override
protected void onStop() {
super.onStop();
mapView.onStop();
}

@Override
public void onPause() {
super.onPause();
mapView.onPause();
}

@Override
public void onLowMemory() {
super.onLowMemory();
mapView.onLowMemory();
}

@Override
protected void onDestroy() {
super.onDestroy();
mapView.onDestroy();
}

@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
mapView.onSaveInstanceState(outState);
}
}