/*
* This is part of Geomajas, a GIS framework, http://www.geomajas.org/.
*
* Copyright 2008-2015 Geosparc nv, http://www.geosparc.com/, Belgium.
*
* The program is available in open source according to the GNU Affero
* General Public License. All contributions in this program are covered
* by the Geomajas Contributors License Agreement. For full licensing
* details, see LICENSE.txt in the project root.
*/
package org.geomajas.plugin.editing.client.handler;
import java.util.Collections;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.geomajas.geometry.Coordinate;
import org.geomajas.geometry.Geometry;
import org.geomajas.gwt.client.handler.MapDragHandler;
import org.geomajas.gwt.client.handler.MapUpHandler;
import org.geomajas.plugin.editing.client.operation.GeometryOperationFailedException;
import org.geomajas.plugin.editing.client.service.GeometryEditState;
import org.geomajas.plugin.editing.client.service.GeometryIndex;
import org.geomajas.plugin.editing.client.service.GeometryIndexNotFoundException;
import org.geomajas.plugin.editing.client.service.GeometryIndexType;
import com.google.gwt.event.dom.client.HumanInputEvent;
import com.google.gwt.event.dom.client.MouseMoveEvent;
import com.google.gwt.event.dom.client.MouseMoveHandler;
import com.google.gwt.event.dom.client.MouseOutEvent;
import com.google.gwt.event.dom.client.MouseOutHandler;
import com.google.gwt.event.dom.client.MouseOverEvent;
import com.google.gwt.event.dom.client.MouseOverHandler;
/**
* <p>
* When a single selected vertex is dragged over this vertex, than it will snap to it. At this point, this handler will
* stop any further propagation of the events, to make sure the base controllers can't mess up. As a results it is
* recommended to install this snapping handler as the latest vertex handler.
* </p>
* <p>
* At this moment only vertex snapping is supported, but later on, this may be applied to edges as well.
* </p>
*
* @author Pieter De Graef
* @author Jan Venstermans
*/
public class GeometryIndexSnapToDeleteHandler extends AbstractGeometryIndexMapHandler implements MouseOverHandler,
MouseOutHandler, MapDragHandler, MapUpHandler, MouseMoveHandler {
private static Logger logger = Logger.getLogger(GeometryIndexSnapToDeleteHandler.class.getName());
private boolean allowMoreThanNeighbours;
private GeometryIndex indexToDelete;
public GeometryIndexSnapToDeleteHandler() {
this(true);
}
public GeometryIndexSnapToDeleteHandler(boolean allowMoreThanNeighbours) {
this.allowMoreThanNeighbours = allowMoreThanNeighbours;
}
public void onMouseOver(MouseOverEvent event) {
checkHover(event);
}
public void onMouseOut(MouseOutEvent event) {
service.getIndexStateService().markForDeletionEnd(Collections.singletonList(index));
}
public void onMouseMove(MouseMoveEvent event) {
if (service.getIndexStateService().isMarkedForDeletion(index)) {
event.stopPropagation();
}
}
public void onDrag(HumanInputEvent<?> event) {
checkHover(event);
}
public void onUp(HumanInputEvent<?> event) {
if (service.getIndexStateService().isMarkedForDeletion(index)) {
// If marked for deletion, remove on mouse up:
try {
service.getIndexStateService().markForDeletionEnd(Collections.singletonList(index));
List<GeometryIndex> toDelete = Collections.singletonList(indexToDelete);
service.getIndexStateService().markForDeletionEnd(toDelete);
service.getIndexStateService().deselectAll();
service.remove(toDelete);
} catch (GeometryOperationFailedException e) {
logger.log(Level.WARNING, "Operation failed", e);
}
}
}
/**
* Supports only vertices for now.
*/
private void checkHover(HumanInputEvent<?> event) {
// Check: editing state, selection (there must be 1 index selected, but not this one):
if (service.getEditingState() == GeometryEditState.DRAGGING
&& !service.getIndexStateService().isSelected(index)
&& service.getIndexStateService().getSelection().size() == 1) {
GeometryIndex selected = service.getIndexStateService().getSelection().get(0);
// Check: is the selected index of the same type, and is it a neighbor?
if (service.getIndexService().getType(index) == service.getIndexService().getType(selected)) {
// see if there are enough vertices left to delete one:
int siblingCount = service.getIndexService().getSiblingCount(service.getGeometry(), index);
try {
String geometryType = service.getIndexService().getGeometryType(service.getGeometry(), index);
if (geometryType.equals(Geometry.LINE_STRING)) {
if (siblingCount < 3) {
return; // 2 vertices is the minimum for a LineString.
}
} else if (geometryType.equals(Geometry.LINEAR_RING)) {
if (siblingCount < 5) {
return; // 4 vertices is the minimum for a LinearRing.
}
} else {
throw new IllegalStateException("Illegal type of geometry found.");
}
} catch (GeometryIndexNotFoundException e) {
throw new IllegalStateException(e);
}
if (!allowMoreThanNeighbours &&
!(service.getIndexService().isAdjacent(service.getGeometry(), index, selected))) {
// only allow neighbours to remove point + hovering over a not-neighbour
return;
}
// Mark the selected GeometryIndex for deletion:
indexToDelete = selected;
if (!service.getIndexStateService().isMarkedForDeletion(index)) {
service.getIndexStateService().markForDeletionBegin(Collections.singletonList(index));
}
// Than snap the selected vertex/edge to this one:
// if this where omitted, the selected vertex would not overlay index position.
if (service.getIndexService().getType(index) == GeometryIndexType.TYPE_VERTEX) {
try {
Coordinate location = service.getIndexService().getVertex(service.getGeometry(), index);
service.move(Collections.singletonList(selected),
Collections.singletonList(Collections.singletonList(location)));
event.stopPropagation();
} catch (GeometryIndexNotFoundException e) {
logger.log(Level.WARNING, "Index not found", e);
} catch (GeometryOperationFailedException e) {
logger.log(Level.WARNING, "Operation failed", e);
}
}
}
}
}
}