gt: 			GeometryIndex insertIndex = editService.getInsertIndex();
context: /*
   * 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.gwt2.plugin.editing.client.gfx;
  
  import org.geomajas.geometry.Bbox;
  import org.geomajas.geometry.Coordinate;
  import org.geomajas.geometry.Geometry;
  import org.geomajas.gwt.client.map.RenderSpace;
  import org.geomajas.gwt2.client.GeomajasImpl;
  import org.geomajas.gwt2.client.controller.MapController;
  import org.geomajas.gwt2.client.event.ViewPortChangedEvent;
  import org.geomajas.gwt2.client.event.ViewPortChangedHandler;
  import org.geomajas.gwt2.client.gfx.ShapeStyle;
  import org.geomajas.gwt2.client.gfx.VectorContainer;
  import org.geomajas.gwt2.client.map.MapPresenter;
  import org.geomajas.plugin.editing.client.event.GeometryEditChangeStateEvent;
  import org.geomajas.plugin.editing.client.event.GeometryEditChangeStateHandler;
  import org.geomajas.plugin.editing.client.event.GeometryEditMoveEvent;
  import org.geomajas.plugin.editing.client.event.GeometryEditMoveHandler;
  import org.geomajas.plugin.editing.client.event.GeometryEditResumeEvent;
  import org.geomajas.plugin.editing.client.event.GeometryEditResumeHandler;
  import org.geomajas.plugin.editing.client.event.GeometryEditShapeChangedEvent;
  import org.geomajas.plugin.editing.client.event.GeometryEditShapeChangedHandler;
  import org.geomajas.plugin.editing.client.event.GeometryEditStartEvent;
  import org.geomajas.plugin.editing.client.event.GeometryEditStartHandler;
  import org.geomajas.plugin.editing.client.event.GeometryEditStopEvent;
  import org.geomajas.plugin.editing.client.event.GeometryEditStopHandler;
  import org.geomajas.plugin.editing.client.event.GeometryEditSuspendEvent;
  import org.geomajas.plugin.editing.client.event.GeometryEditSuspendHandler;
  import org.geomajas.plugin.editing.client.event.GeometryEditTentativeMoveEvent;
  import org.geomajas.plugin.editing.client.event.GeometryEditTentativeMoveHandler;
  import org.geomajas.plugin.editing.client.event.state.GeometryIndexDeselectedEvent;
  import org.geomajas.plugin.editing.client.event.state.GeometryIndexDeselectedHandler;
  import org.geomajas.plugin.editing.client.event.state.GeometryIndexDisabledEvent;
  import org.geomajas.plugin.editing.client.event.state.GeometryIndexDisabledHandler;
  import org.geomajas.plugin.editing.client.event.state.GeometryIndexEnabledEvent;
  import org.geomajas.plugin.editing.client.event.state.GeometryIndexEnabledHandler;
  import org.geomajas.plugin.editing.client.event.state.GeometryIndexHighlightBeginEvent;
  import org.geomajas.plugin.editing.client.event.state.GeometryIndexHighlightBeginHandler;
  import org.geomajas.plugin.editing.client.event.state.GeometryIndexHighlightEndEvent;
  import org.geomajas.plugin.editing.client.event.state.GeometryIndexHighlightEndHandler;
  import org.geomajas.plugin.editing.client.event.state.GeometryIndexMarkForDeletionBeginEvent;
  import org.geomajas.plugin.editing.client.event.state.GeometryIndexMarkForDeletionBeginHandler;
  import org.geomajas.plugin.editing.client.event.state.GeometryIndexMarkForDeletionEndEvent;
  import org.geomajas.plugin.editing.client.event.state.GeometryIndexMarkForDeletionEndHandler;
  import org.geomajas.plugin.editing.client.event.state.GeometryIndexSelectedEvent;
  import org.geomajas.plugin.editing.client.event.state.GeometryIndexSelectedHandler;
  import org.geomajas.plugin.editing.client.gfx.GeometryRenderer;
  import org.geomajas.plugin.editing.client.handler.EdgeMapHandlerFactory;
  import org.geomajas.plugin.editing.client.handler.VertexMapHandlerFactory;
  import org.geomajas.plugin.editing.client.service.GeometryEditService;
  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 org.vaadin.gwtgraphics.client.VectorObject;
  import org.vaadin.gwtgraphics.client.shape.Path;
  import org.vaadin.gwtgraphics.client.shape.path.LineTo;
  import org.vaadin.gwtgraphics.client.shape.path.MoveTo;
  
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  
  /**
   * Renderer for geometries during the editing process.
   *
   * @author Pieter De Graef
   * @author Jan De Moerloose
   */
  public class GeometryRendererImpl implements GeometryRenderer, GeometryEditStartHandler, GeometryEditStopHandler,
  		GeometryEditSuspendHandler, GeometryEditResumeHandler, GeometryIndexHighlightBeginHandler,
  		GeometryIndexHighlightEndHandler, GeometryEditMoveHandler, GeometryEditShapeChangedHandler,
  		GeometryEditChangeStateHandler, GeometryIndexSelectedHandler, GeometryIndexDeselectedHandler,
  		GeometryIndexDisabledHandler, GeometryIndexEnabledHandler, GeometryIndexMarkForDeletionBeginHandler,
  		GeometryIndexMarkForDeletionEndHandler, GeometryEditTentativeMoveHandler, ViewPortChangedHandler {
  
  	private final MapPresenter mapPresenter;
  
  	private final GeometryEditService editService;
  
  	private final StyleProvider styleProvider;
  
  	private final Map<GeometryIndex, VectorObject> shapes;
  
  	private GeometryIndexShapeFactory shapeFactory;
  
  	private GeometryIndexStyleFactory styleFactory;
  
  	private GeometryIndexControllerFactory controllerFactory;
  
  	private VectorContainer container;
  
  	private Path tentativeMoveLine;
  
  	private VectorObject nullShape;
  
  	// ------------------------------------------------------------------------
  	// Constructor:
  	// ------------------------------------------------------------------------
  
  	/**
  	 * Create a new renderer instance that renders on the given map and listens to the given editing service.
  	 *
  	 * @param mapPresenter The map to render on.
  	 * @param editService  The geometry editing service to listen to.
  	 */
  	public GeometryRendererImpl(MapPresenter mapPresenter, GeometryEditService editService) {
  		this.mapPresenter = mapPresenter;
  		this.editService = editService;
  		this.styleProvider = new StyleProvider();
  		this.shapes = new HashMap<GeometryIndex, VectorObject>();
  
  		// Initialize default factories:
  		this.shapeFactory = new DefaultGeometryIndexShapeFactory(mapPresenter, RenderSpace.SCREEN);
  		this.styleFactory = new DefaultGeometryIndexStyleFactory(styleProvider);
  		this.controllerFactory = new DefaultGeometryIndexControllerFactory(mapPresenter);
  
  		// Add ViewPortChangedHandler:
  		mapPresenter.getEventBus().addHandler(ViewPortChangedHandler.TYPE, this);
  
  		// Add edit base handlers:
  		editService.addGeometryEditChangeStateHandler(this);
  		editService.addGeometryEditMoveHandler(this);
  		editService.addGeometryEditShapeChangedHandler(this);
  		editService.addGeometryEditStartHandler(this);
  		editService.addGeometryEditStopHandler(this);
  		editService.addGeometryEditSuspendHandler(this);
  		editService.addGeometryEditResumeHandler(this);
  		editService.addGeometryEditTentativeMoveHandler(this);
  
  		// Add GeometryIndex state handlers:
  		editService.getIndexStateService().addGeometryIndexDeselectedHandler(this);
  		editService.getIndexStateService().addGeometryIndexSelectedHandler(this);
  		editService.getIndexStateService().addGeometryIndexDisabledHandler(this);
  		editService.getIndexStateService().addGeometryIndexEnabledHandler(this);
  		editService.getIndexStateService().addGeometryIndexHighlightBeginHandler(this);
  		editService.getIndexStateService().addGeometryIndexHighlightEndHandler(this);
  		editService.getIndexStateService().addGeometryIndexMarkForDeletionBeginHandler(this);
  		editService.getIndexStateService().addGeometryIndexMarkForDeletionEndHandler(this);
  	}
  
  	// ------------------------------------------------------------------------
  	// Public methods:
  	// ------------------------------------------------------------------------
  
  	/**
  	 * Clear everything and completely redraw the edited geometry.
  	 */
  	public void redraw() {
  		shapes.clear();
  		if (container != null) {
  			container.setTranslation(0, 0);
  			container.clear();
  			try {
  				tentativeMoveLine = new Path(-5, -5);
  				tentativeMoveLine.lineTo(-5, -5);
  				ShapeStyle style = styleProvider.getEdgeTentativeMoveStyle();
  				GeomajasImpl.getInstance().getGfxUtil().applyStyle(tentativeMoveLine, style);
  				container.add(tentativeMoveLine);
  				draw();
  			} catch (GeometryIndexNotFoundException e) {
  				// Happens when creating new geometries...can't render points that don't exist yet.
  			}
  		}
  	}
  
  	// ------------------------------------------------------------------------
  	// ViewPortChangedHandler implementation:
  	// ------------------------------------------------------------------------
  
  	/**
  	 * Redraw the geometry on the map.
  	 */
  	public void onViewPortChanged(ViewPortChangedEvent event) {
  		redraw();
  	}
  
  	// ------------------------------------------------------------------------
  	// Start & stop handler implementations:
  	// ------------------------------------------------------------------------
  
  	/**
  	 * Clean up the previous state, create a container to draw in and then draw the geometry.
  	 */
  	public void onGeometryEditStart(GeometryEditStartEvent event) {
  		if (container != null) {
  			mapPresenter.getContainerManager().removeVectorContainer(container);
  		}
  		container = mapPresenter.getContainerManager().addScreenContainer();
  		redraw();
  	}
  
  	/**
  	 * Clean up all rendering.
  	 */
  	public void onGeometryEditStop(GeometryEditStopEvent event) {
  		// Remove the vector container from the map:
  		mapPresenter.getContainerManager().removeVectorContainer(container);
  		container = null;
  		shapes.clear();
  	}
  
  	@Override
  	public void onGeometryEditSuspend(GeometryEditSuspendEvent event) {
  		if (container != null) {
  			mapPresenter.getContainerManager().removeVectorContainer(container);
  		}
  		container = mapPresenter.getContainerManager().addScreenContainer();
  		redraw();
  	}
  
  	@Override
  	public void onGeometryEditResume(GeometryEditResumeEvent event) {
  		if (container != null) {
  			mapPresenter.getContainerManager().removeVectorContainer(container);
  		}
  		container = mapPresenter.getContainerManager().addScreenContainer();
  		redraw();
  	}
  
  	// ------------------------------------------------------------------------
  	// GeometryIndex state change implementations:
  	// ------------------------------------------------------------------------
  
  	/**
  	 * Update the styles of the geometry indices in the event.
  	 */
  	public void onGeometryIndexMarkForDeletionEnd(GeometryIndexMarkForDeletionEndEvent event) {
  		for (GeometryIndex index : event.getIndices()) {
  			update(index, false);
  		}
  	}
  
  	/**
  	 * Update the styles of the geometry indices in the event.
  	 */
  	public void onGeometryIndexMarkForDeletionBegin(GeometryIndexMarkForDeletionBeginEvent event) {
  		for (GeometryIndex index : event.getIndices()) {
  			update(index, false);
  		}
  	}
  
  	/**
  	 * Update the styles of the geometry indices in the event.
  	 */
  	public void onGeometryIndexEnabled(GeometryIndexEnabledEvent event) {
  		for (GeometryIndex index : event.getIndices()) {
  			update(index, false);
  		}
  	}
  
  	/**
  	 * Update the styles of the geometry indices in the event.
  	 */
  	public void onGeometryIndexDisabled(GeometryIndexDisabledEvent event) {
  		for (GeometryIndex index : event.getIndices()) {
  			update(index, false);
  		}
  	}
  
  	/**
  	 * Update the styles of the geometry indices in the event.
  	 */
  	public void onGeometryIndexDeselected(GeometryIndexDeselectedEvent event) {
  		for (GeometryIndex index : event.getIndices()) {
  			update(index, false);
  		}
  	}
  
  	/**
  	 * Update the styles of the geometry indices in the event.
  	 */
  	public void onGeometryIndexSelected(GeometryIndexSelectedEvent event) {
  		for (GeometryIndex index : event.getIndices()) {
  			update(index, false);
  		}
  	}
  
  	/**
  	 * Update the styles of the geometry indices in the event.
  	 */
  	public void onGeometryIndexHighlightEnd(GeometryIndexHighlightEndEvent event) {
  		for (GeometryIndex index : event.getIndices()) {
  			update(index, false);
  		}
  	}
  
  	/**
  	 * Update the styles of the geometry indices in the event.
  	 */
  	public void onGeometryIndexHighlightBegin(GeometryIndexHighlightBeginEvent event) {
  		for (GeometryIndex index : event.getIndices()) {
  			update(index, false);
  		}
  	}
  
  	// ------------------------------------------------------------------------
  	// GeometryEditChangeStateHandler implementation:
  	// ------------------------------------------------------------------------
  
  	/**
  	 * Change the cursor while dragging.
  	 */
  	public void onChangeEditingState(GeometryEditChangeStateEvent event) {
  		switch (event.getEditingState()) {
  			case DRAGGING:
  				mapPresenter.setCursor("move");
  				break;
  			case IDLE:
  			default:
  				mapPresenter.setCursor("default");
  				redraw();
  		}
  	}
  
  	// ------------------------------------------------------------------------
  	// Geometry shape change implementation:
  	// ------------------------------------------------------------------------
  
  	/**
  	 * Redraw the geometry on the map.
  	 */
  	public void onGeometryShapeChanged(GeometryEditShapeChangedEvent event) {
  		redraw();
  	}
  
  	/**
  	 * Figure out what's being moved and update only adjacent objects. This is far more performing than simply redrawing
  	 * everything.
  	 */
  	public void onGeometryEditMove(GeometryEditMoveEvent event) {
  		// Find the elements that need updating:
  		Map<GeometryIndex, Boolean> indicesToUpdate = new HashMap<GeometryIndex, Boolean>();
  		for (GeometryIndex index : event.getIndices()) {
  			if (!indicesToUpdate.containsKey(index)) {
  				indicesToUpdate.put(index, false);
  				if (!Geometry.POINT.equals(editService.getGeometry().getGeometryType())
  						&& !Geometry.MULTI_POINT.equals(editService.getGeometry().getGeometryType())) {
  					try {
  						List<GeometryIndex> neighbors;
  						switch (editService.getIndexService().getType(index)) {
  							case TYPE_VERTEX:
  								// Move current vertex to the back. This helps the delete operation.
  								indicesToUpdate.put(index, true);
  								neighbors = editService.getIndexService().getAdjacentEdges(event.getGeometry(), index);
  								if (neighbors != null) {
  									for (GeometryIndex neighborIndex : neighbors) {
  										if (!indicesToUpdate.containsKey(neighborIndex)) {
  											indicesToUpdate.put(neighborIndex, false);
  										}
  									}
  								}
  
  								neighbors = editService.getIndexService().getAdjacentVertices(event.getGeometry(),
  										index);
  								if (neighbors != null) {
  									for (GeometryIndex neighborIndex : neighbors) {
  										if (!indicesToUpdate.containsKey(neighborIndex)) {
  											indicesToUpdate.put(neighborIndex, false);
  										}
  									}
  								}
  								break;
  							case TYPE_EDGE:
  								neighbors = editService.getIndexService().getAdjacentVertices(event.getGeometry(),
  										index);
  								if (neighbors != null) {
  									for (GeometryIndex neighborIndex : neighbors) {
  										if (!indicesToUpdate.containsKey(neighborIndex)) {
  											indicesToUpdate.put(neighborIndex, false);
  										}
  									}
  								}
  								break;
  							default:
  						}
  					} catch (GeometryIndexNotFoundException e) {
  						throw new IllegalStateException(e);
  					}
  				}
  			}
  		}
  
  		// Check if we need to draw the background (nice, but slows down):
  		if (styleProvider.getBackgroundStyle() != null && styleProvider.getBackgroundStyle().getFillOpacity() > 0) {
  			if (event.getGeometry().getGeometryType().equals(Geometry.POLYGON)) {
  				update(null, false);
  			} else if (event.getGeometry().getGeometryType().equals(Geometry.MULTI_POLYGON)
  					&& event.getGeometry().getGeometries() != null) {
  				for (int i = 0; i < event.getGeometry().getGeometries().length; i++) {
  					GeometryIndex index = editService.getIndexService().create(GeometryIndexType.TYPE_GEOMETRY, i);
  					indicesToUpdate.put(index, false);
  				}
  			}
  		}
  
  		// Next, redraw the list:
  		for (GeometryIndex index : indicesToUpdate.keySet()) {
  			update(index, indicesToUpdate.get(index));
  		}
  	}
  
  	/**
  	 * Renders a line from the last inserted point to the current mouse position, indicating what the new situation
  	 * would look like if a vertex where to be inserted at the mouse location.
  	 */
  	public void onTentativeMove(GeometryEditTentativeMoveEvent event) {
  		try {
  			Coordinate[] vertices = editService.getIndexService().getSiblingVertices(editService.getGeometry(),
  					editService.getInsertIndex());
  			String geometryType = editService.getIndexService().getGeometryType(editService.getGeometry(),
  					editService.getInsertIndex());
  
  			if (vertices != null
  					&& (Geometry.LINE_STRING.equals(geometryType) || Geometry.LINEAR_RING.equals(geometryType))) {
  				Coordinate temp1 = event.getOrigin();
  				Coordinate temp2 = event.getCurrentPosition();
  				Coordinate c1 = mapPresenter.getViewPort().getTransformationService()
  						.transform(temp1, RenderSpace.WORLD, RenderSpace.SCREEN);
  				Coordinate c2 = mapPresenter.getViewPort().getTransformationService()
  						.transform(temp2, RenderSpace.WORLD, RenderSpace.SCREEN);
  
  				tentativeMoveLine.setStep(0, new MoveTo(false, c1.getX(), c1.getY()));
  				tentativeMoveLine.setStep(1, new LineTo(false, c2.getX(), c2.getY()));
  			} else if (vertices != null && Geometry.LINEAR_RING.equals(geometryType)) {
  				// Draw the second line (as an option...)
  			}
  		} catch (GeometryIndexNotFoundException e) {
  			throw new IllegalStateException(e);
  		}
  	}
  
  	// ------------------------------------------------------------------------
  	// Getters and setters:
  	// ------------------------------------------------------------------------
  
  	/**
  	 * Get the style provider. This object is used by the default style factory for providing styles for the geometry
  	 * rendering. As long as the default style factory is being used, this object can be used to change the styles.
  	 */
  	public StyleProvider getStyleProvider() {
  		return styleProvider;
  	}
  
  	public void setVisible(boolean visible) {
  		if (container != null) {
  			container.setVisible(visible);
  		}
  	}
  
  	@Override
  	public void addVertexHandlerFactory(VertexMapHandlerFactory factory) {
  		((DefaultGeometryIndexControllerFactory) controllerFactory).addVertexHandlerFactory(factory);
  	}
  
  	@Override
  	public void addEdgeHandlerFactory(EdgeMapHandlerFactory factory) {
  		((DefaultGeometryIndexControllerFactory) controllerFactory).addEdgeHandlerFactory(factory);
  	}
  
  	// ------------------------------------------------------------------------
  	// Private methods for updating:
  	// ------------------------------------------------------------------------
  
  	private void update(GeometryIndex index, boolean moveToBack) {
  		try {
  			VectorObject shape;
  			if (index == null) {
  				shape = nullShape;
  			} else {
  				shape = shapes.get(index);
  			}
  			if (shape != null) {
  				// We don't consider position at this point. Just style:
  				ShapeStyle style = styleFactory.create(editService, index);
  				GeomajasImpl.getInstance().getGfxUtil().applyStyle(shape, style);
  
  				// Now update the location:
  				shapeFactory.update(shape, editService, index);
  
  				// Move to the front if requested:
  				if (moveToBack) {
  					container.moveToBack(shape);
  				}
  			}
  		} catch (GeometryIndexNotFoundException e) {
  			throw new IllegalStateException(e);
  		}
  	}
  
  	// ------------------------------------------------------------------------
  	// Private methods for re-drawing:
  	// ------------------------------------------------------------------------
  
  	private void draw() throws GeometryIndexNotFoundException {
  		if (Geometry.POINT.equals(editService.getGeometry().getGeometryType())) {
  			drawPoint(null);
  		} else if (Geometry.LINE_STRING.equals(editService.getGeometry().getGeometryType())
  				|| Geometry.LINEAR_RING.equals(editService.getGeometry().getGeometryType())) {
  			drawLineString(null);
  		} else if (Geometry.POLYGON.equals(editService.getGeometry().getGeometryType())) {
  			drawPolygon(null);
  		}
  	}
  
  	private void drawPolygon(GeometryIndex parentIndex) throws GeometryIndexNotFoundException {
  		Geometry geometry = editService.getGeometry();
  		if (parentIndex != null) {
  			geometry = editService.getIndexService().getGeometry(editService.getGeometry(), parentIndex);
  		}
  		if (geometry.getGeometries() != null) {
  			// First of all we check if the background needs to be drawn:
  			if (styleProvider.getBackgroundStyle() != null && styleProvider.getBackgroundStyle().getFillOpacity() > 0) {
  				drawIndex(parentIndex);
  			}
  
  			// Then, we draw all LinearRings one by one:
  			for (int i = 0; i < geometry.getGeometries().length; i++) {
  				GeometryIndex index = editService.getIndexService().addChildren(parentIndex,
  						GeometryIndexType.TYPE_GEOMETRY, i);
  				drawLinearRing(index);
  			}
  		}
  	}
  
  	private void drawLinearRing(GeometryIndex parentIndex) throws GeometryIndexNotFoundException {
  		Geometry geometry = editService.getGeometry();
  		if (parentIndex != null) {
  			geometry = editService.getIndexService().getGeometry(editService.getGeometry(), parentIndex);
  		}
  		int[] indices = null;
  		if (geometry.getCoordinates() != null) {
  			int max = geometry.getCoordinates().length - 1;
  			boolean inserting = false;
  			boolean limited = false;
  			
  			// If we are inserting in this particular LinearRing, don't display the closing edge/vertex or it 
  			// looks like the ring is already closed
  