This repository was archived by the owner on Jan 26, 2023. It is now read-only.
This repository was archived by the owner on Jan 26, 2023. It is now read-only.
sketch-editor, drag freehand polyline, no drop... #528
Open
Description
The sketch-editor appears to have a bug when dragging previously captured freehand polylines.
Problem summary: When editing an existing (i.e., previously saved) freehand polyline - it is never "dropped" after being dragged.
Problem:
Start the sketch-editor by passing in an existing freehand polyline graphic from the GraphicsOverlay. (mSketchEditor.start(existingFreehandPolygonGraphic.getGeometry(),sketchCreationMode.FREEHAND_LINE)
) Tap to select the freehand polyline. Tap and hold to drag it to another location. Lift your finger to stop dragging - the polyline is never "dropped", i.e., it is still in a dragged state (dashed lines).
The code below was based on the sketch-editor example:
package com.esri.arcgisruntime.sample.sketcheditor;
import android.content.Context;
import android.graphics.Color;
import android.os.Bundle;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.widget.ImageButton;
import android.widget.TextView;
import com.esri.arcgisruntime.concurrent.ListenableFuture;
import com.esri.arcgisruntime.geometry.Geometry;
import com.esri.arcgisruntime.geometry.GeometryEngine;
import com.esri.arcgisruntime.geometry.GeometryType;
import com.esri.arcgisruntime.geometry.Point;
import com.esri.arcgisruntime.geometry.SpatialReferences;
import com.esri.arcgisruntime.mapping.ArcGISMap;
import com.esri.arcgisruntime.mapping.Basemap;
import com.esri.arcgisruntime.mapping.popup.Popup;
import com.esri.arcgisruntime.mapping.view.Callout;
import com.esri.arcgisruntime.mapping.view.DefaultMapViewOnTouchListener;
import com.esri.arcgisruntime.mapping.view.Graphic;
import com.esri.arcgisruntime.mapping.view.GraphicsOverlay;
import com.esri.arcgisruntime.mapping.view.IdentifyGraphicsOverlayResult;
import com.esri.arcgisruntime.mapping.view.MapView;
import com.esri.arcgisruntime.mapping.view.SketchCreationMode;
import com.esri.arcgisruntime.mapping.view.SketchEditConfiguration;
import com.esri.arcgisruntime.mapping.view.SketchEditor;
import com.esri.arcgisruntime.mapping.view.SketchGeometryChangedEvent;
import com.esri.arcgisruntime.mapping.view.SketchGeometryChangedListener;
import com.esri.arcgisruntime.symbology.SimpleFillSymbol;
import com.esri.arcgisruntime.symbology.SimpleLineSymbol;
import com.esri.arcgisruntime.symbology.SimpleMarkerSymbol;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
public class MainActivity extends AppCompatActivity {
private final String TAG = MainActivity.class.getSimpleName();
private SimpleMarkerSymbol mPointSymbol;
private SimpleLineSymbol mLineSymbol, mLineSymbol2;
private SimpleFillSymbol mFillSymbol, mFillSymbol2;
private MapView mMapView;
private SketchEditor mSketchEditor;
private GraphicsOverlay mGraphicsOverlay;
private MenuItem cancel, stop, redo, undo;
private Callout mCallout;
private boolean bSketchEdit = false;
private Graphic originalGraphic;
private ImageButton mPointButton;
private ImageButton mMultiPointButton;
private ImageButton mPolylineButton;
private ImageButton mPolygonButton;
private ImageButton mFreehandLineButton;
private ImageButton mFreehandPolygonButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
this.setTitle("");
// define symbols
mPointSymbol = new SimpleMarkerSymbol(SimpleMarkerSymbol.Style.SQUARE, Color.RED, 20);
mLineSymbol2 = new SimpleLineSymbol(SimpleLineSymbol.Style.SOLID, 0xFFFF8800, 4);
mLineSymbol = new SimpleLineSymbol(SimpleLineSymbol.Style.SOLID, Color.MAGENTA, 4, SimpleLineSymbol.MarkerStyle.ARROW, SimpleLineSymbol.MarkerPlacement.END);
mFillSymbol = new SimpleFillSymbol(SimpleFillSymbol.Style.CROSS, Color.CYAN, new SimpleLineSymbol(SimpleLineSymbol.Style.SOLID, Color.BLUE, 4));
mFillSymbol2 = new SimpleFillSymbol(SimpleFillSymbol.Style.CROSS, Color.GREEN, mLineSymbol2);
// inflate map view from layout
mMapView = findViewById(R.id.mapView);
// create a map with the Basemap Type topographic
ArcGISMap map = new ArcGISMap(Basemap.Type.LIGHT_GRAY_CANVAS, 34.056295, -117.195800, 16);
// set the map to be displayed in this view
mMapView.setMap(map);
mGraphicsOverlay = new GraphicsOverlay();
mMapView.getGraphicsOverlays().add(mGraphicsOverlay);
mMapView.setOnTouchListener(new MapSingleTapListener(this, mMapView));
// create a new sketch editor and add it to the map view
mSketchEditor = new SketchEditor();
mMapView.setSketchEditor(mSketchEditor);
mSketchEditor.addGeometryChangedListener(new SketchGeometryChangedListener() {
@Override
public void geometryChanged(SketchGeometryChangedEvent sketchGeometryChangedEvent) {
if (mSketchEditor != null) {
cancel.setEnabled(true);
stop.setEnabled(bSketchEdit || mSketchEditor.isSketchValid()); //TODO better way?
undo.setEnabled(mSketchEditor.canUndo());
redo.setEnabled(mSketchEditor.canRedo());
}
}
});
// get buttons from layouts
mPointButton = findViewById(R.id.pointButton);
mMultiPointButton = findViewById(R.id.pointsButton);
mPolylineButton = findViewById(R.id.polylineButton);
mPolygonButton = findViewById(R.id.polygonButton);
mFreehandLineButton = findViewById(R.id.freehandLineButton);
mFreehandPolygonButton = findViewById(R.id.freehandPolygonButton);
// add click listeners
mPointButton.setOnClickListener(view -> createModePoint());
mMultiPointButton.setOnClickListener(view -> createModeMultipoint());
mPolylineButton.setOnClickListener(view -> createModePolyline());
mPolygonButton.setOnClickListener(view -> createModePolygon());
mFreehandLineButton.setOnClickListener(view -> createModeFreehandLine());
mFreehandPolygonButton.setOnClickListener(view -> createModeFreehandPolygon());
}
/**
* When the point button is clicked, reset other buttons, show the point button as selected, and start point
* drawing mode.
*/
private void createModePoint() {
resetButtons();
mPointButton.setSelected(true);
mSketchEditor.stop(); // will this fix a bug with changing draw modes w/o stopping
mSketchEditor.start(SketchCreationMode.POINT);
}
/**
* When the multipoint button is clicked, reset other buttons, show the multipoint button as selected, and start
* multipoint drawing mode.
*/
private void createModeMultipoint() {
resetButtons();
mMultiPointButton.setSelected(true);
mSketchEditor.stop(); // will this fix a bug with changing draw modes w/o stopping
mSketchEditor.start(SketchCreationMode.MULTIPOINT);
}
/**
* When the polyline button is clicked, reset other buttons, show the polyline button as selected, and start
* polyline drawing mode.
*/
private void createModePolyline() {
resetButtons();
mPolylineButton.setSelected(true);
mSketchEditor.stop(); // will this fix a bug with changing draw modes w/o stopping
mSketchEditor.start(SketchCreationMode.POLYLINE);
}
/**
* When the polygon button is clicked, reset other buttons, show the polygon button as selected, and start polygon
* drawing mode.
*/
private void createModePolygon() {
resetButtons();
mPolygonButton.setSelected(true);
mSketchEditor.stop(); // will this fix a bug with changing draw modes w/o stopping
mSketchEditor.start(SketchCreationMode.POLYGON);
}
/**
* When the freehand line button is clicked, reset other buttons, show the freehand line button as selected, and
* start freehand line drawing mode.
*/
private void createModeFreehandLine() {
resetButtons();
mFreehandLineButton.setSelected(true);
mSketchEditor.stop(); // will this fix a bug with changing draw modes w/o stopping
mSketchEditor.start(SketchCreationMode.FREEHAND_LINE);
}
/**
* When the freehand polygon button is clicked, reset other buttons, show the freehand polygon button as selected,
* and enable freehand polygon drawing mode.
*/
private void createModeFreehandPolygon() {
resetButtons();
mFreehandPolygonButton.setSelected(true);
mSketchEditor.stop(); // will this fix a bug with changing draw modes w/o stopping
mSketchEditor.start(SketchCreationMode.FREEHAND_POLYGON);
}
/**
* When the undo button is clicked, undo the last event on the SketchEditor.
*/
private void undo() {
if (mSketchEditor.canUndo()) {
mSketchEditor.undo();
}
}
/**
* When the redo button is clicked, redo the last undone event on the SketchEditor.
*/
private void redo() {
if (mSketchEditor.canRedo()) {
mSketchEditor.redo();
}
}
/**
* When the stop button is clicked, check that sketch is valid. If so, get the geometry from the sketch, set its
* symbol and add it to the graphics overlay.
*/
private void stop() {
bSketchEdit = false;
originalGraphic = null;
if (!mSketchEditor.isSketchValid()) {
reportNotValid();
mSketchEditor.stop();
resetButtons();
disableMenuOptions();
return;
}
// get the geometry from sketch editor
Geometry sketchGeometry = mSketchEditor.getGeometry();
// mSketchEditor.stop();
// resetButtons();
if (sketchGeometry != null) {
// create a graphic from the sketch editor geometry
Map<String, Object> mapAttributes = new HashMap<String, Object>();
mapAttributes.put("GeometryType", sketchGeometry.getGeometryType().name());
mapAttributes.put("SketchCreationMode", mSketchEditor.getSketchCreationMode().name());
Graphic graphic = new Graphic(sketchGeometry, mapAttributes);
// assign a symbol based on geometry type
if (graphic.getGeometry().getGeometryType() == GeometryType.POLYGON) {
graphic.setSymbol(mFillSymbol);
} else if (graphic.getGeometry().getGeometryType() == GeometryType.POLYGON
&& mSketchEditor.getSketchCreationMode().equals(SketchCreationMode.FREEHAND_POLYGON)) {
graphic.setSymbol(mFillSymbol2);
} else if (graphic.getGeometry().getGeometryType() == GeometryType.POLYLINE
&& mSketchEditor.getSketchCreationMode().equals(SketchCreationMode.FREEHAND_LINE)) {
graphic.setSymbol(mLineSymbol2);
} else if (graphic.getGeometry().getGeometryType() == GeometryType.POLYLINE) {
graphic.setSymbol(mLineSymbol);
} else if (graphic.getGeometry().getGeometryType() == GeometryType.POINT ||
graphic.getGeometry().getGeometryType() == GeometryType.MULTIPOINT) {
graphic.setSymbol(mPointSymbol);
}
// add the graphic to the graphics overlay
mGraphicsOverlay.getGraphics().add(graphic);
}
mSketchEditor.stop();
resetButtons();
disableMenuOptions();
}
/**
* When the cancel button is clicked, cancel the SketchEditor.
*/
private void cancel() {
mSketchEditor.stop();
resetButtons();
if (bSketchEdit) {
// add the graphic to the graphics overlay
mGraphicsOverlay.getGraphics().add(originalGraphic);
originalGraphic = null;
}
bSketchEdit = false;
disableMenuOptions();
}
/**
* Called if sketch is invalid. Reports to user why the sketch was invalid.
*/
private void reportNotValid() {
String validIf;
if (mSketchEditor.getSketchCreationMode() == SketchCreationMode.POINT) {
validIf = "Point only valid if it contains an x & y coordinate.";
} else if (mSketchEditor.getSketchCreationMode() == SketchCreationMode.MULTIPOINT) {
validIf = "Multipoint only valid if it contains at least one vertex.";
} else if (mSketchEditor.getSketchCreationMode() == SketchCreationMode.POLYLINE
|| mSketchEditor.getSketchCreationMode() == SketchCreationMode.FREEHAND_LINE) {
validIf = "Polyline only valid if it contains at least one part of 2 or more vertices.";
} else if (mSketchEditor.getSketchCreationMode() == SketchCreationMode.POLYGON
|| mSketchEditor.getSketchCreationMode() == SketchCreationMode.FREEHAND_POLYGON) {
validIf = "Polygon only valid if it contains at least one part of 3 or more vertices which form a closed ring.";
} else {
validIf = "No sketch creation mode selected.";
}
String report = "Sketch geometry invalid:\n" + validIf;
Snackbar reportSnackbar = Snackbar.make(findViewById(R.id.toolbarInclude), report, Snackbar.LENGTH_INDEFINITE);
reportSnackbar.setAction("Dismiss", view -> reportSnackbar.dismiss());
TextView snackbarTextView = reportSnackbar.getView().findViewById(android.support.design.R.id.snackbar_text);
snackbarTextView.setSingleLine(false);
reportSnackbar.show();
Log.e(TAG, report);
}
/**
* De-selects all buttons.
*/
private void resetButtons() {
mPointButton.setSelected(false);
mMultiPointButton.setSelected(false);
mPolylineButton.setSelected(false);
mPolygonButton.setSelected(false);
mFreehandLineButton.setSelected(false);
mFreehandPolygonButton.setSelected(false);
}
/**
* Disable menu options
*/
private void disableMenuOptions() {
this.cancel.setEnabled(false);
if (bSketchEdit) {
this.stop.setEnabled(true); //TODO maybe do nothing here?
} else {
this.stop.setEnabled(false);
}
this.undo.setEnabled(false);
this.redo.setEnabled(false);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.undo_redo_stop_menu, menu);
this.cancel = menu.findItem(R.id.cancel);
this.stop = menu.findItem(R.id.stop);
this.undo = menu.findItem(R.id.undo);
this.redo = menu.findItem(R.id.redo);
disableMenuOptions();
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.undo) {
undo();
} else if (id == R.id.redo) {
redo();
} else if (id == R.id.stop) {
stop();
} else if (id == R.id.cancel) {
cancel();
}
return super.onOptionsItemSelected(item);
}
@Override
protected void onPause() {
mMapView.pause();
super.onPause();
}
@Override
protected void onResume() {
super.onResume();
mMapView.resume();
}
@Override
protected void onDestroy() {
super.onDestroy();
mMapView.dispose();
}
class MapSingleTapListener extends DefaultMapViewOnTouchListener {
public MapSingleTapListener(Context context, MapView mapView) {
super(context, mapView);
}
@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
// get the screen point where user tapped
android.graphics.Point screenPoint = new android.graphics.Point((int) e.getX(), (int) e.getY());
final Point mapPoint = mMapView.screenToLocation(screenPoint);
// convert to WGS84 for lat/lon format
final Point wgs84Point = (Point) GeometryEngine.project(mapPoint, SpatialReferences.getWgs84());
Log.d(TAG, String.format("Lat: %.6f, Lon: %.6f)", wgs84Point.getY(), wgs84Point.getX()));
// create a selection tolerance
int tolerance = 10;
double mapTolerance = tolerance * mMapView.getUnitsPerDensityIndependentPixel();
// identify graphics on the graphics overlay
final ListenableFuture<IdentifyGraphicsOverlayResult> identifyGraphic = mMapView.identifyGraphicsOverlayAsync(mGraphicsOverlay, screenPoint, tolerance, false, 4);
identifyGraphic.addDoneListener(new Runnable() {
@Override
public void run() {
try {
IdentifyGraphicsOverlayResult grOverlayResult = identifyGraphic.get();
List<Popup> popup = grOverlayResult.getPopups();
// get the list of graphics returned by identify graphic overlay
List<Graphic> graphic = grOverlayResult.getGraphics();
// get size of list in results
int identifyResultSize = graphic.size();
String message = "Tapped on " + identifyResultSize + " graphic%1$s";
if (!graphic.isEmpty()) { // show a toast message if graphic was returned
message = String.format(message, (identifyResultSize == 1 ? "" : "s"));
Log.d(TAG, message);
int i = 0;
for (Graphic gr : graphic) {
// Log.d(TAG, "(" + (i + 1) + ") " + gr.getGeometry().toJson());
// message += "\n(" + (i + 1) + "): " + gr.getAttributes().get("name").toString();
for (Map.Entry<String, Object> entry : gr.getAttributes().entrySet()) {
message += "\n(" + (i + 1) + ") " + entry.getKey() + " = " + entry.getValue().toString();
}
i++;
// Log.d(TAG, message);
}
// For now, just work with 1st Graphic object...
originalGraphic = graphic.get(0);
Map<String, Object> mapAttributes = originalGraphic.getAttributes();
if (mapAttributes != null && mapAttributes.containsKey("GeometryType") && mapAttributes.containsKey("SketchCreationMode")) {
Log.d(TAG, "GeometryType: " + mapAttributes.get("GeometryType").toString());
Log.d(TAG, "SketchCreationMode: " + mapAttributes.get("SketchCreationMode").toString());
GeometryType geometryType = GeometryType.valueOf(mapAttributes.get("GeometryType").toString());
SketchCreationMode sketchCreationMode = SketchCreationMode.valueOf(mapAttributes.get("sketchCreationMode").toString());
if (sketchCreationMode.equals(SketchCreationMode.POINT)) {
mPointButton.setSelected(true);
} else if (sketchCreationMode.equals(SketchCreationMode.MULTIPOINT)) {
mMultiPointButton.setSelected(true);
} else if (sketchCreationMode.equals(SketchCreationMode.POLYLINE)) {
mPolylineButton.setSelected(true);
} else if (sketchCreationMode.equals(SketchCreationMode.POLYGON)) {
mPolygonButton.setSelected(true);
} else if (sketchCreationMode.equals(SketchCreationMode.FREEHAND_LINE)) {
mFreehandLineButton.setSelected(true);
} else if (sketchCreationMode.equals(SketchCreationMode.FREEHAND_POLYGON)) {
mFreehandPolygonButton.setSelected(true);
}
SketchEditConfiguration sketchEditConfiguration = new SketchEditConfiguration();
mSketchEditor.start(originalGraphic.getGeometry(), sketchCreationMode, sketchEditConfiguration);
// mSketchEditor.start(originalGraphic.getGeometry(), sketchCreationMode);
// mSketchEditor.start(originalGraphic.getGeometry());
mGraphicsOverlay.getGraphics().remove(originalGraphic);
bSketchEdit = true;
}
}
} catch (InterruptedException | ExecutionException ie) {
ie.printStackTrace();
}
}
});
return super.onSingleTapConfirmed(e);
}
}
}
Metadata
Metadata
Assignees
Labels
No labels