/* * Geopaparazzi - Digital field mapping on Android based devices * Copyright (C) 2016 HydroloGIS (www.hydrologis.com) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package eu.geopaparazzi.core.maptools.tools; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Point; import android.graphics.PointF; import android.graphics.PorterDuff.Mode; import android.preference.PreferenceManager; import android.view.MotionEvent; import android.view.View; import android.view.View.OnClickListener; import android.view.View.OnTouchListener; import android.view.ViewGroup.LayoutParams; import android.widget.ImageButton; import android.widget.LinearLayout; import android.widget.Toast; import com.vividsolutions.jts.android.PointTransformation; import com.vividsolutions.jts.android.ShapeWriter; import com.vividsolutions.jts.geom.Coordinate; import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.geom.MultiPoint; import org.mapsforge.android.maps.MapView; import org.mapsforge.android.maps.MapViewPosition; import org.mapsforge.android.maps.Projection; import org.mapsforge.core.model.GeoPoint; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import eu.geopaparazzi.library.database.GPLog; import eu.geopaparazzi.library.features.EditManager; import eu.geopaparazzi.library.features.EditingView; import eu.geopaparazzi.library.features.Feature; import eu.geopaparazzi.library.features.ILayer; import eu.geopaparazzi.library.features.Tool; import eu.geopaparazzi.library.features.ToolGroup; import eu.geopaparazzi.library.style.ColorUtilities; import eu.geopaparazzi.library.style.ToolColors; import eu.geopaparazzi.library.util.Compat; import eu.geopaparazzi.library.util.GPDialogs; import eu.geopaparazzi.library.util.LibraryConstants; import eu.geopaparazzi.library.util.PositionUtilities; import eu.geopaparazzi.spatialite.database.spatial.core.daos.DaoSpatialite; import eu.geopaparazzi.spatialite.database.spatial.core.layers.SpatialVectorTableLayer; import eu.geopaparazzi.spatialite.database.spatial.util.JtsUtilities; import eu.geopaparazzi.core.GeopaparazziApplication; import eu.geopaparazzi.core.R; import eu.geopaparazzi.core.maptools.FeatureUtilities; import eu.geopaparazzi.core.mapview.MapsSupportService; import eu.geopaparazzi.core.mapview.overlays.MapsforgePointTransformation; import eu.geopaparazzi.core.mapview.overlays.SliderDrawProjection; import static java.lang.Math.round; /** * The group of tools active when a selection has been done. * * @author Cesar Martinez (www.scolab.es) */ public class PointCreateFeatureToolGroup implements ToolGroup, OnClickListener, OnTouchListener, View.OnLongClickListener { private final MapView mapView; private Feature featureToContinue; private int buttonSelectionColor; private List<Coordinate> coordinatesList = new ArrayList<>(); private ImageButton addVertexButton; private SliderDrawProjection editingViewProjection; private final Paint createdGeometryPaintHaloStroke = new Paint(); private final Paint createdGeometryPaintStroke = new Paint(); /** * Stores the top-left map position at which the redraw should happen. */ private final Point point; /** * Stores the map position after drawing is finished. */ private Point positionBeforeDraw; private ImageButton gpsStreamButton; private ImageButton commitButton; private ImageButton undoButton; private Geometry pointGeometry; private boolean gpsStreamActive = false; private ImageButton addVertexByTapButton; private boolean addVertexByTapActive = false; private Coordinate lastKnownGpsCoordinate; /** * Constructor. * * @param mapView the map view. */ public PointCreateFeatureToolGroup(MapView mapView, Feature featureToContinue) { this.mapView = mapView; this.featureToContinue = featureToContinue; if (this.featureToContinue != null) { // go get the last inserted feature by rowid try { pointGeometry = FeatureUtilities.WKBREADER.read(this.featureToContinue.getDefaultGeometry()); List<Coordinate> oldCoords = Arrays.asList(pointGeometry.getCoordinates()); coordinatesList.addAll(oldCoords); } catch (Exception e) { GPLog.error(this, null, e); this.featureToContinue = null; } } EditingView editingView = EditManager.INSTANCE.getEditingView(); editingViewProjection = new SliderDrawProjection(mapView, editingView); buttonSelectionColor = Compat.getColor(editingView.getContext(), R.color.main_selection); int selectionStroke = ColorUtilities.toColor(ToolColors.selection_stroke.getHex()); createdGeometryPaintHaloStroke.setAntiAlias(true); createdGeometryPaintHaloStroke.setStrokeWidth(7f); createdGeometryPaintHaloStroke.setColor(Color.BLACK); createdGeometryPaintHaloStroke.setStyle(Paint.Style.STROKE); createdGeometryPaintStroke.setAntiAlias(true); createdGeometryPaintStroke.setStrokeWidth(5f); createdGeometryPaintStroke.setColor(selectionStroke); createdGeometryPaintStroke.setStyle(Paint.Style.STROKE); point = new Point(); positionBeforeDraw = new Point(); } public void activate() { if (mapView != null) mapView.setClickable(true); } public void initUI() { LinearLayout parent = EditManager.INSTANCE.getToolsLayout(); parent.removeAllViews(); Context context = parent.getContext(); ILayer editLayer = EditManager.INSTANCE.getEditLayer(); int padding = 2; if (editLayer != null) { gpsStreamButton = new ImageButton(context); gpsStreamButton.setLayoutParams(new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)); gpsStreamButton.setBackground(Compat.getDrawable(context, R.drawable.editing_add_point_gps_off)); gpsStreamButton.setPadding(0, padding, 0, padding); gpsStreamButton.setOnTouchListener(this); gpsStreamButton.setOnLongClickListener(this); gpsStreamButton.setOnClickListener(this); parent.addView(gpsStreamButton); addVertexButton = new ImageButton(context); addVertexButton.setLayoutParams(new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)); addVertexButton.setBackground(Compat.getDrawable(context, R.drawable.editing_add_point_center)); addVertexButton.setPadding(0, padding, 0, padding); addVertexButton.setOnTouchListener(this); addVertexButton.setOnClickListener(this); parent.addView(addVertexButton); addVertexByTapButton = new ImageButton(context); addVertexByTapButton.setLayoutParams(new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)); addVertexByTapButton.setBackground(Compat.getDrawable(context, R.drawable.editing_add_point_tap_off)); addVertexByTapButton.setPadding(0, padding, 0, padding); addVertexByTapButton.setOnTouchListener(this); addVertexByTapButton.setOnClickListener(this); parent.addView(addVertexByTapButton); undoButton = new ImageButton(context); undoButton.setLayoutParams(new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)); undoButton.setBackground(Compat.getDrawable(context, R.drawable.editing_undo)); undoButton.setPadding(0, padding, 0, padding); undoButton.setOnTouchListener(this); undoButton.setOnClickListener(this); parent.addView(undoButton); commitButton = new ImageButton(context); commitButton.setLayoutParams(new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)); commitButton.setBackground(Compat.getDrawable(context, R.drawable.editing_commit)); commitButton.setPadding(0, padding, 0, padding); commitButton.setOnTouchListener(this); commitButton.setOnClickListener(this); commitButton.setVisibility(View.GONE); parent.addView(commitButton); } } public void disable() { LinearLayout parent = EditManager.INSTANCE.getToolsLayout(); if (parent != null) parent.removeAllViews(); parent = null; gpsStreamActive = false; addVertexByTapActive = false; } @Override public boolean onLongClick(View v) { if (v == gpsStreamButton) { gpsStreamActive = !gpsStreamActive; addVertexByTapActive = false; } EditManager.INSTANCE.invalidateEditingView(); handleToolIcons(v); return true; } public void onClick(View v) { if (v == addVertexButton) { // adds point in map center gpsStreamActive = false; addVertexByTapActive = false; final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(GeopaparazziApplication .getInstance()); double[] mapCenter = PositionUtilities.getMapCenterFromPreferences(preferences, true, true); Coordinate coordinate = new Coordinate(mapCenter[0], mapCenter[1]); addVertex(v.getContext(), coordinate); } else if (v == gpsStreamButton) { addVertexByTapActive = false; addGpsCoordinate(); } else if (v == addVertexByTapButton) { addVertexByTapActive = !addVertexByTapActive; gpsStreamActive = false; } else if (v == commitButton) { if (coordinatesList.size() > 0) { List<Geometry> geomsList = new ArrayList<>(); MultiPoint pointGeometry = JtsUtilities.createPoints(coordinatesList); geomsList.add(pointGeometry); ILayer editLayer = EditManager.INSTANCE.getEditLayer(); if (editLayer instanceof SpatialVectorTableLayer) { SpatialVectorTableLayer spatialVectorTableLayer = (SpatialVectorTableLayer) editLayer; try { if (this.featureToContinue == null) { for (Geometry geometry : geomsList) { DaoSpatialite.addNewFeatureByGeometry(geometry, LibraryConstants.SRID_WGS84_4326, spatialVectorTableLayer.getSpatialVectorTable()); } } else { DaoSpatialite.updateFeatureGeometry( featureToContinue.getId(), pointGeometry, LibraryConstants.SRID_WGS84_4326, spatialVectorTableLayer.getSpatialVectorTable()); } GPDialogs.toast(commitButton.getContext(), commitButton.getContext().getString(R.string.geometry_saved), Toast.LENGTH_SHORT); coordinatesList.clear(); // reset mapview Context context = v.getContext(); Intent intent = new Intent(context, MapsSupportService.class); intent.putExtra(MapsSupportService.REREAD_MAP_REQUEST, true); context.startService(intent); } catch (jsqlite.Exception e) { if (e.getMessage().contains("UNIQUE constraint failed")) { GPDialogs.warningDialog(commitButton.getContext(), commitButton.getContext().getString(R.string.unique_constraint_violation_message), null); coordinatesList.clear(); this.pointGeometry = null; } else { GPLog.error(this, null, e); } } } } } else if (v == undoButton) { if (coordinatesList.size() == 0) { EditManager.INSTANCE.setActiveToolGroup(new PointMainEditingToolGroup(mapView)); EditManager.INSTANCE.setActiveTool(null); return; } else if (coordinatesList.size() > 0) { int size = coordinatesList.size() - 1; coordinatesList.remove(size); // commitButton.setVisibility(View.VISIBLE); reCreateGeometry(v.getContext()); } } if (coordinatesList.size() > 0) { commitButton.setVisibility(View.VISIBLE); } else { commitButton.setVisibility(View.GONE); } EditManager.INSTANCE.invalidateEditingView(); handleToolIcons(v); } private void addVertex(Context context, Coordinate coordinate) { coordinatesList.clear(); coordinatesList.add(coordinate); int coordinatesCount = coordinatesList.size(); reCreateGeometry(context); } private void reCreateGeometry(Context context) { pointGeometry = null; if (coordinatesList.size() > 0) { pointGeometry = JtsUtilities.createPoints(coordinatesList); } } @SuppressWarnings("deprecation") private void handleToolIcons(View activeToolButton) { Context context = activeToolButton.getContext(); if (gpsStreamButton != null) if (gpsStreamActive) { gpsStreamButton .setBackground(Compat.getDrawable(context, R.drawable.editing_add_point_gps_on)); } else { gpsStreamButton.setBackground(Compat.getDrawable(context, R.drawable.editing_add_point_gps_off)); gpsStreamButton.getBackground().clearColorFilter(); } if (addVertexByTapButton != null) if (addVertexByTapActive) { addVertexByTapButton.setBackground(Compat.getDrawable(context, R.drawable.editing_add_point_tap_on)); } else { addVertexByTapButton.setBackground(Compat.getDrawable(context, R.drawable.editing_add_point_tap_off)); } } public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: { v.getBackground().setColorFilter(buttonSelectionColor, Mode.SRC_ATOP); v.invalidate(); break; } case MotionEvent.ACTION_UP: { v.getBackground().clearColorFilter(); v.invalidate(); break; } } return false; } public void onToolFinished(Tool tool) { // nothing } public void onToolDraw(Canvas canvas) { try { Projection projection = editingViewProjection; byte zoomLevelBeforeDraw; synchronized (mapView) { zoomLevelBeforeDraw = mapView.getMapPosition().getZoomLevel(); positionBeforeDraw = projection.toPoint(mapView.getMapPosition().getMapCenter(), positionBeforeDraw, zoomLevelBeforeDraw); } // calculate the top-left point of the visible rectangle point.x = positionBeforeDraw.x - (canvas.getWidth() >> 1); point.y = positionBeforeDraw.y - (canvas.getHeight() >> 1); MapViewPosition mapPosition = mapView.getMapPosition(); byte zoomLevel = mapPosition.getZoomLevel(); PointTransformation pointTransformer = new MapsforgePointTransformation(projection, point, zoomLevel); ShapeWriter shapeWriter = new ShapeWriter(pointTransformer); shapeWriter.setRemoveDuplicatePoints(true); // shapeWriter.setDecimation(spatialTable.getStyle().decimationFactor); // draw features if (pointGeometry != null) { FeatureUtilities.drawGeometry(pointGeometry, canvas, shapeWriter, null, createdGeometryPaintStroke); } final PointF vertexPoint = new PointF(); /* if (coordinatesList.size() == 2) { final PointF vertexPoint2 = new PointF(); pointTransformer.transform(coordinatesList.get(0), vertexPoint); pointTransformer.transform(coordinatesList.get(1), vertexPoint2); canvas.drawLine(vertexPoint.x, vertexPoint.y, vertexPoint2.x, vertexPoint2.y, createdGeometryPaintHaloStroke); canvas.drawLine(vertexPoint.x, vertexPoint.y, vertexPoint2.x, vertexPoint2.y, createdGeometryPaintStroke); }*/ for (Coordinate vertexCoordinate : coordinatesList) { pointTransformer.transform(vertexCoordinate, vertexPoint); canvas.drawCircle(vertexPoint.x, vertexPoint.y, 10f, createdGeometryPaintHaloStroke); canvas.drawCircle(vertexPoint.x, vertexPoint.y, 10f, createdGeometryPaintStroke); } } catch (Exception e) { GPLog.error(this, null, e); } } public boolean onToolTouchEvent(MotionEvent event) { if (addVertexByTapActive) { if (mapView == null) { return false; } Projection pj = mapView.getProjection(); float currentX = event.getX(); float currentY = event.getY(); int action = event.getAction(); if (action == MotionEvent.ACTION_DOWN) { GeoPoint tapGeoPoint = pj.fromPixels(round(currentX), round(currentY)); Coordinate coordinate = new Coordinate(tapGeoPoint.getLongitude(), tapGeoPoint.getLatitude()); addVertex(mapView.getContext(), coordinate); if (coordinatesList.size() > 0) { commitButton.setVisibility(View.VISIBLE); } else { commitButton.setVisibility(View.GONE); } EditManager.INSTANCE.invalidateEditingView(); return true; } } return false; } public void onGpsUpdate(double lon, double lat) { lastKnownGpsCoordinate = new Coordinate(lon, lat); if (gpsStreamActive) { addGpsCoordinate(); } } private void addGpsCoordinate() { if (lastKnownGpsCoordinate != null) { addVertex(mapView.getContext(), lastKnownGpsCoordinate); if (coordinatesList.size() > 0) { commitButton.setVisibility(View.VISIBLE); } else { commitButton.setVisibility(View.GONE); } EditManager.INSTANCE.invalidateEditingView(); } else { EditingView editingView = EditManager.INSTANCE.getEditingView(); Context context = editingView.getContext(); GPDialogs.warningDialog(context, context.getString(R.string.no_gps_coordinate_acquired_yet), null); } } }