Skip to content
This repository has been archived by the owner on Jan 26, 2023. It is now read-only.

sketch-editor, drag freehand polyline, no drop... #528

Open
celoftis opened this issue Sep 15, 2018 · 1 comment
Open

sketch-editor, drag freehand polyline, no drop... #528

celoftis opened this issue Sep 15, 2018 · 1 comment
Assignees

Comments

@celoftis
Copy link

celoftis commented Sep 15, 2018

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);
        }
    }
}
@celoftis celoftis changed the title sketch-editor, drag freehand polygon, no drop... sketch-editor, drag freehand polyline, no drop... Sep 15, 2018
@TADraeseke TADraeseke self-assigned this Sep 21, 2018
@TADraeseke
Copy link
Contributor

Thanks @celoftis - I'll look into this.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants