using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using Core.Common.Utils;
using Core.Common.Utils.Collections;
using Core.GIS.GeoApi.CoordinateSystems.Transformations;
using Core.GIS.GeoApi.Extensions.Feature;
using Core.GIS.GeoApi.Geometries;
using Core.GIS.NetTopologySuite.Geometries;
using Core.GIS.SharpMap.Api.Editors;
using Core.GIS.SharpMap.Api.Layers;
using Core.GIS.SharpMap.Data.Providers;
using Core.GIS.SharpMap.Layers;
using Core.GIS.SharpMap.Rendering;
using Core.GIS.SharpMap.Rendering.Thematics;
using Core.GIS.SharpMap.Styles;
using Core.GIS.SharpMap.UI.Forms;
using Core.GIS.SharpMap.UI.Helpers;
using Core.GIS.SharpMap.UI.Properties;
using log4net;
using GeometryFactory = Core.GIS.SharpMap.Converters.Geometries.GeometryFactory;
namespace Core.GIS.SharpMap.UI.Tools
{
public enum MultiSelectionMode
{
Rectangle = 0,
Lasso
}
///
/// SelectTool enables users to select features in the map
/// The current implementation supports:
/// - single selection feature by click on feature
/// - multiple selection of feature by dragging a rectangle
/// - adding features to the selection (KeyExtendSelection; normally the SHIFT key)
/// - toggling selection of features (KeyToggleSelection; normally the CONTROL key)
/// if featues is not in selection it is added to selection
/// if feature is in selection it is removed from selection
/// - Selection is visible to the user via Trackers. Features with an IPoint geometry have 1
/// tracker, based on ILineString and IPolygon have a tracker for each coordinate
/// - Trackers can have focus.
/// If a Trackers has focus is visible to the user via another symbol (or same symbol in other color)
/// A tracker that has the focus is the tracker leading during special operation such as moving.
/// For single selection a feature with an IPoint geometry automatically get the focus to the
/// only tracker
/// - Multiple Trackers with focus
/// - adding focus Trackers (KeyExtendSelection; normally the SHIFT key)
/// - * KeyExtendSelection can be used to select all branches between the most recent two selected branches,
/// using shortest path.
/// - toggling focus Trackers (KeyToggleSelection; normally the CONTROL key)
/// - Selection cycling, When multiple features overlap clicking on a selected feature will
/// result in the selection of the next feature. Compare behavior in Sobek Netter.
///
/// TODO
/// - functionality reasonably ok, but TOO complex : refactor using tests
/// - Selection cycling can be improved:
/// - for a ILineString the focus tracker is not set initially which can be set in the second
/// click. Thus a ILineString (and IPolygon) can eat a click
/// - if feature must be taken into account by selection cycling should be an option
/// (topology rule?)
///
public class SelectTool : MapTool
{
public event EventHandler SelectionChanged;
public static int MaxSelectedFeatures = 5000;
private static readonly ILog log = LogManager.GetLogger(typeof(SelectTool));
private static readonly IDictionary stylesCache = new Dictionary();
private readonly Collection trackers = new Collection();
private readonly List selectPoints = new List();
private readonly VectorLayer trackingLayer;
private DateTime orgClickTime;
private ICoordinate mouseDownLocation; // TODO: remove me
private ICoordinate orgMouseDownLocation;
private ICoordinate WORLDPOSITION;
private bool isMultiSelect;
public SelectTool()
{
orgClickTime = DateTime.Now;
Name = "Select";
SelectedFeatureInteractors = new List();
trackingLayer = new VectorLayer("Trackers")
{
DataSource = new FeatureCollection
{
Features = trackers
},
Theme = new CustomTheme(GetTrackerStyle)
};
}
public override Cursor Cursor
{
get
{
switch (MultiSelectionMode)
{
case MultiSelectionMode.Rectangle:
return Cursors.Default;
case MultiSelectionMode.Lasso:
return MapCursors.CreateCursor(Resources.lassoselect, 0, 0);
default:
throw new ArgumentOutOfRangeException();
}
}
}
public override IMapControl MapControl
{
get
{
return base.MapControl;
}
set
{
base.MapControl = value;
trackingLayer.Map = MapControl.Map;
}
}
public override bool IsActive
{
get
{
return base.IsActive;
}
set
{
base.IsActive = value;
if (!IsActive)
{
MultiSelectionMode = MultiSelectionMode.Rectangle;
}
if (MapControl != null)
{
MapControl.Cursor = Cursors.Default;
}
}
}
public MultiSelectionMode MultiSelectionMode { get; set; }
///
/// Interactors created for selected features.
///
public IList SelectedFeatureInteractors { get; private set; }
public bool KeyToggleSelection
{
get
{
return ((Control.ModifierKeys & Keys.Control) == Keys.Control);
}
}
public bool KeyExtendSelection
{
get
{
return ((Control.ModifierKeys & Keys.Shift) == Keys.Shift);
}
}
public IEnumerable Selection
{
get
{
return SelectedFeatureInteractors.Select(interactor => interactor.SourceFeature);
}
}
public TrackerFeature GetTrackerAtCoordinate(ICoordinate worldPos)
{
TrackerFeature trackerFeature = null;
foreach (var featureInteractor in SelectedFeatureInteractors)
{
var coordinate = worldPos;
if (featureInteractor.Layer != null && featureInteractor.Layer.CoordinateTransformation != null)
{
var mathTransform = featureInteractor.Layer.CoordinateTransformation.MathTransform.Inverse();
coordinate = TransformCoordinate(worldPos, mathTransform);
}
trackerFeature = featureInteractor.GetTrackerAtCoordinate(coordinate);
if (trackerFeature != null)
{
break;
}
}
return trackerFeature;
}
public void Clear()
{
Clear(true);
}
public IFeatureInteractor GetFeatureInteractor(ILayer layer, IFeature feature)
{
try
{
return layer.FeatureEditor == null ? null : layer.FeatureEditor.CreateInteractor(layer, feature);
}
catch (Exception exception)
{
log.Error("Error creating feature interactor: " + exception.Message);
}
return null;
}
public void AddSelection(IEnumerable features)
{
foreach (var feature in features)
{
var layer = Map.GetLayerByFeature(feature);
if (layer == null)
{
throw new ArgumentOutOfRangeException("features", "Can't find layer for feature: " + feature);
}
AddSelection(layer, feature, false);
}
UpdateMapControlSelection();
}
public void AddSelection(ILayer layer, IFeature feature, bool synchronizeUI = true, bool checkIfAlreadySelected = true)
{
// If already selected, do nothing.
if (!layer.Visible || (checkIfAlreadySelected && Selection.Contains(feature)))
{
return;
}
var featureInteractor = GetFeatureInteractor(layer, feature);
if (featureInteractor == null)
{
return;
}
SelectedFeatureInteractors.Add(featureInteractor);
if (synchronizeUI)
{
UpdateMapControlSelection();
}
}
///
/// Selects the given features on the map. Will search all layers for the features when no vector layer is provided
///
/// The feature to select on the map.
/// The layer on which the features reside.
public bool Select(IEnumerable featuresToSelect, ILayer vectorLayer = null, bool checkIfAlreadySelected = true)
{
if (featuresToSelect == null)
{
Clear(true);
return false;
}
var features = featuresToSelect as IList ?? featuresToSelect.ToList();
if (features.Count > MaxSelectedFeatures)
{
log.Warn(string.Format("Can't select {0} features at once, selecting only first {1} features.", features.Count, MaxSelectedFeatures));
features = features.Take(MaxSelectedFeatures).ToList();
}
Clear(false);
VectorLayer foundLayer = null;
//var selectionFailsBecauseFeatureCannotBeFoundInLayer = new List();
var warningMessages = new List();
var inLayer = false;
foreach (var feature in features)
{
if (vectorLayer != null)
{
foundLayer = vectorLayer as VectorLayer;
inLayer = foundLayer != null && foundLayer.DataSource.Features.Contains(feature);
}
else if (foundLayer == null || foundLayer.DataSource == null || foundLayer.DataSource.Features == null)
{
foundLayer = Map.GetLayerByFeature(feature) as VectorLayer;
inLayer = foundLayer != null;
}
else
{
inLayer = foundLayer.DataSource.Features.Contains(feature);
if (!inLayer)
{
foundLayer = Map.GetLayerByFeature(feature) as VectorLayer;
inLayer = foundLayer != null;
}
}
if (foundLayer != null)
{
if (inLayer)
{
AddSelection(foundLayer, feature, ReferenceEquals(feature, features.Last()), checkIfAlreadySelected);
}
else
{
var message = string.Format("The feature '{1}' you want to select is NOT in the layer '{0}'",
foundLayer.Name,
feature is INameable ? ((INameable) feature).Name : feature.ToString());
warningMessages.Add(message);
}
}
}
if (warningMessages.Count > 0)
{
log.Warn(string.Join(Environment.NewLine, warningMessages));
}
return true;
}
///
/// Selects the given feature on the map. Will search all layers for the feature.
///
/// The feature to select on the map.
public bool Select(IFeature featureToSelect)
{
if (null == featureToSelect)
{
Clear(true);
return false;
}
// Find the layer that this feature is on
ILayer foundLayer = MapControl.Map.GetLayerByFeature(featureToSelect);
if (foundLayer != null && foundLayer is VectorLayer)
{
// Select the feature
Select(foundLayer, featureToSelect);
return true;
}
return false;
}
public void Select(ILayer vectorLayer, IFeature feature)
{
if (IsBusy)
{
return;
}
Clear(false);
SetSelection(feature, vectorLayer);
UpdateMapControlSelection(true);
}
///
/// Checks if selected features are actually need to be selected.
///
public void RefreshSelection()
{
var visibleLayers = Map.GetAllVisibleLayers(true).ToList();
// check if selected features still exist in the providers
SelectedFeatureInteractors.Where(featureInteractor => featureInteractor.Layer.DataSource == null || !featureInteractor.Layer.DataSource.Features.Contains(featureInteractor.SourceFeature)).ToArray()
.ForEach(fi => SelectedFeatureInteractors.Remove(fi));
SelectedFeatureInteractors.RemoveAllWhere(i => !visibleLayers.Contains(i.Layer));
UpdateMapControlSelection();
}
public override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
Render(e.Graphics, MapControl.Map);
}
public override void Render(Graphics graphics, Map.Map map)
{
// Render the selectionLayer and trackingLayer
// Bypass ILayer.Render and call OnRender directly; this is more efficient
foreach (var tracker in trackers.Where(tracker => tracker.FeatureInteractor.SourceFeature != null))
{
// todo optimize this; only necessary when map extent has changed.
var interactor = tracker.FeatureInteractor;
var feature = interactor.TargetFeature ?? interactor.SourceFeature;
interactor.UpdateTracker(feature.Geometry);
}
SynchronizeTrackers();
trackingLayer.OnRender(graphics, map);
}
public override IEnumerable GetContextMenuItems(ICoordinate worldPosition)
{
var selectFeatureMenu = CreateContextMenuItemForFeaturesAtLocation(worldPosition, "Select", Select, false);
if (selectFeatureMenu == null || selectFeatureMenu.DropDownItems.Count == 0)
{
yield break;
}
yield return new MapToolContextMenuItem
{
Priority = 1,
MenuItem = selectFeatureMenu
};
}
public override void OnDraw(Graphics graphics)
{
var color = KeyExtendSelection ? Color.Magenta : Color.DeepSkyBlue;
if (MultiSelectionMode == MultiSelectionMode.Lasso)
{
var points = selectPoints.ToArray();
if (points.Length < 2)
{
return;
}
using (var pen = new Pen(color))
{
graphics.DrawCurve(pen, points);
}
using (var brush = new SolidBrush(Color.FromArgb(30, color)))
{
graphics.FillClosedCurve(brush, points);
}
}
else
{
var point1 = Map.WorldToImage(GeometryFactory.CreateCoordinate(mouseDownLocation.X, mouseDownLocation.Y));
var point2 = Map.WorldToImage(GeometryFactory.CreateCoordinate(WORLDPOSITION.X, WORLDPOSITION.Y));
var rectangle = new Rectangle((int) Math.Min(point1.X, point2.X),
(int) Math.Min(point1.Y, point2.Y),
(int) Math.Abs(point1.X - point2.X),
(int) Math.Abs(point1.Y - point2.Y));
using (var pen = new Pen(color))
{
graphics.DrawRectangle(pen, rectangle);
}
using (var brush = new SolidBrush(Color.FromArgb(30, color)))
{
graphics.FillRectangle(brush, rectangle);
}
}
}
public override void OnMouseDown(ICoordinate worldPosition, MouseEventArgs e)
{
if (e.Button != MouseButtons.Left)
{
return;
}
mouseDownLocation = worldPosition;
orgMouseDownLocation = null;
IsBusy = true;
var trackerFeature = SelectedFeatureInteractors.Count <= 1
? GetTrackerAtCoordinate(worldPosition)
: null; // hack: if multiple selection toggle/select complete feature
if (trackerFeature != null)
{
if (SelectedFeatureInteractors.Count != 1)
{
return;
}
orgMouseDownLocation = (ICoordinate) worldPosition.Clone();
FocusTracker(trackerFeature);
MapControl.Refresh();
return;
}
ILayer selectedLayer;
var limit = (float) MapHelper.ImageToWorld(Map, 4);
var nearest = FindNearestFeature(worldPosition, limit, out selectedLayer, ol => ol.Visible);
if (nearest != null)
{
SelectFeature(worldPosition, nearest, selectedLayer);
}
else
{
if (!KeyExtendSelection)
{
Clear(false);
}
StartMultiSelect();
}
MapControl.Refresh();
}
public override void OnMouseMove(ICoordinate worldPosition, MouseEventArgs e)
{
if (!isMultiSelect)
{
return;
}
UpdateMultiSelection(worldPosition);
DoDrawing(false);
}
public override void OnMouseDoubleClick(object sender, MouseEventArgs e)
{
orgMouseDownLocation = null;
}
public override void OnMouseUp(ICoordinate worldPosition, MouseEventArgs e)
{
if (e.Button != MouseButtons.Left)
{
return;
}
if (isMultiSelect)
{
StopMultiSelect();
List selectedFeatures = null;
if (!KeyExtendSelection)
{
selectedFeatures = new List(SelectedFeatureInteractors.Select(fe => fe.SourceFeature).ToArray());
Clear(false);
}
var selectionPolygon = CreateSelectionPolygon(worldPosition);
if (selectionPolygon != null)
{
foreach (var layer in Map.GetAllVisibleLayers(false))
{
//make sure parent layer is selectable or null
var parentLayer = Map.GetGroupLayerContainingLayer(layer);
if ((parentLayer == null || parentLayer.IsSelectable) && layer.IsSelectable && layer is VectorLayer)
{
// do not use the maptool provider but the datasource of each layer.
var vectorLayer = (VectorLayer) layer;
var multiFeatures = vectorLayer.GetFeatures(selectionPolygon).Take(MaxSelectedFeatures);
foreach (var feature in multiFeatures)
{
if (selectedFeatures != null && selectedFeatures.Contains(feature))
{
continue;
}
AddSelection(vectorLayer, feature, false,
checkIfAlreadySelected: selectedFeatures == null);
}
}
}
}
else
{
// if mouse hasn't moved handle as single select. A normal multi select uses the envelope
// of the geometry and this has as result that unwanted features will be selected.
ILayer selectedLayer;
var limit = (float) MapHelper.ImageToWorld(Map, 4);
var nearest = FindNearestFeature(worldPosition, limit, out selectedLayer, ol => ol.Visible);
if (nearest != null)
{
AddSelection(selectedLayer, nearest, false);
}
}
// synchronize with map selection, possible check if selection is already set; do not remove
UpdateMapControlSelection(true);
}
else
{
if (orgMouseDownLocation != null && orgMouseDownLocation.X == worldPosition.X && orgMouseDownLocation.Y == worldPosition.Y)
{
// check if mouse was pressed at a selected object without moving the mouse. The default behaviour
// should be to select 'the next' object
TimeSpan timeSpan = DateTime.Now - orgClickTime;
int dc = SystemInformation.DoubleClickTime;
if (dc < timeSpan.TotalMilliseconds)
{
if (1 == SelectedFeatureInteractors.Count)
{
// check if selection exists; could be toggled
ILayer outLayer;
IFeature nextFeature = GetNextFeatureAtPosition(worldPosition,
// set limit from 4 to 10: TOOLS-1499
(float) MapHelper.ImageToWorld(Map, 10),
out outLayer,
SelectedFeatureInteractors[0].SourceFeature,
ol => ol.Visible);
if (null != nextFeature)
{
Clear(false);
SetSelection(nextFeature, outLayer); //-1 for ILineString
//MapControl.Refresh();
}
}
}
}
UpdateMapControlSelection(true);
}
IsBusy = false;
orgClickTime = DateTime.Now;
}
public override void OnMapCollectionChanged(object sender, NotifyCollectionChangingEventArgs e)
{
switch (e.Action)
{
case NotifyCollectionChangeAction.Remove:
{
if (e.Item is ILayer)
{
RefreshSelection();
}
if (sender is Map.Map)
{
var layer = (ILayer) e.Item;
if (layer is GroupLayer)
{
var layerGroup = (GroupLayer) layer;
foreach (ILayer layerGroupLayer in layerGroup.Layers)
{
HandleLayerStatusChanged(layerGroupLayer);
}
}
else
{
HandleLayerStatusChanged(layer);
}
}
break;
}
case NotifyCollectionChangeAction.Replace:
throw new NotImplementedException();
}
}
///
/// todo add cancel method to IMapTool
/// todo mousedown clears selection -> complex selection -> start multi select -> cancel -> original selection lost
///
public override void Cancel()
{
if (IsBusy)
{
if (isMultiSelect)
{
StopMultiSelect();
}
IsBusy = false;
}
Clear(true);
}
///
/// Handles changes to the map (or bubbled up from ITheme, ILayer) properties.
///
///
///
public override void OnMapPropertyChanged(object sender, PropertyChangedEventArgs e)
{
var layer = sender as ILayer;
if (layer != null)
{
if (e.PropertyName == "Visible" && !layer.Visible)
{
RefreshSelection();
}
if (e.PropertyName == "Enabled")
{
// If a layer is enabled of disables and features of the layer are selected
// the selection is cleared. Another solution is to remove only features of layer
// from the selection, but this simple and effective.
if (layer is GroupLayer)
{
var layerGroup = (GroupLayer) layer;
foreach (ILayer layerGroupLayer in layerGroup.Layers)
{
HandleLayerStatusChanged(layerGroupLayer);
}
}
else
{
HandleLayerStatusChanged(layer);
}
}
}
}
protected virtual void SelectFeature(ICoordinate worldPosition, IFeature nearestFeature, ILayer selectedLayer)
{
// Create or add a new FeatureInteractor
if (SelectedFeatureInteractors.Count > 0)
{
var currentFeatureInteractor = GetActiveFeatureInteractor(nearestFeature);
if (KeyExtendSelection) // Shift key
{
if (currentFeatureInteractor == null)
{
AddSelection(selectedLayer, nearestFeature);
}
}
else if (KeyToggleSelection) // CTRL key
{
if (currentFeatureInteractor == null)
{
AddSelection(selectedLayer, nearestFeature);
}
else
{
RemoveSelection(nearestFeature);
}
}
else
{
// no special key processing; handle as a single select.
Clear(false);
if (!StartSelection(selectedLayer, nearestFeature))
{
StartMultiSelect();
}
}
}
else if (!StartSelection(selectedLayer, nearestFeature))
{
StartMultiSelect();
}
}
protected void RemoveSelection(IFeature feature)
{
for (int i = 0; i < SelectedFeatureInteractors.Count; i++)
{
if (ReferenceEquals(SelectedFeatureInteractors[i].SourceFeature, feature))
{
SelectedFeatureInteractors.RemoveAt(i);
break;
}
}
UpdateMapControlSelection();
}
protected IFeatureInteractor GetActiveFeatureInteractor(IFeature feature)
{
return SelectedFeatureInteractors.FirstOrDefault(t => ReferenceEquals(t.SourceFeature, feature));
}
protected void StartMultiSelect()
{
isMultiSelect = true;
selectPoints.Clear();
UpdateMultiSelection(mouseDownLocation);
StartDrawing();
}
protected bool StartSelection(ILayer layer, IFeature feature)
{
var featureInteractor = GetFeatureInteractor(layer, feature);
if (null == featureInteractor)
{
return false;
}
if (featureInteractor.AllowSingleClickAndMove())
{
// do not yet select, but allow MltiSelect
SelectedFeatureInteractors.Add(featureInteractor);
SynchronizeTrackers();
UpdateMapControlSelection();
return true;
}
return false;
}
internal void RefreshFeatureInteractors()
{
var selectedFeaturesWithLayer = SelectedFeatureInteractors.Select(fe => new
{
Feature = fe.SourceFeature, fe.Layer
}).ToList();
SelectedFeatureInteractors.Clear();
selectedFeaturesWithLayer.ForEach(fl => SelectedFeatureInteractors.Add(GetFeatureInteractor(fl.Layer, fl.Feature)));
SynchronizeTrackers();
}
private void StopMultiSelect()
{
isMultiSelect = false;
StopDrawing();
}
///
/// Returns styles used by tracker features.
///
///
///
private static VectorStyle GetTrackerStyle(IFeature feature)
{
var trackerFeature = (TrackerFeature) feature;
VectorStyle style;
// styles are stored in the cache for performance reasons
lock (stylesCache)
{
if (!stylesCache.ContainsKey(trackerFeature.Bitmap))
{
style = new VectorStyle
{
Symbol = trackerFeature.Bitmap
};
stylesCache[trackerFeature.Bitmap] = style;
}
else
{
style = stylesCache[trackerFeature.Bitmap];
}
}
return style;
}
private void Clear(bool fireSelectionChangedEvent)
{
SelectedFeatureInteractors.Clear();
if (trackingLayer.DataSource.GetFeatureCount() <= 0)
{
return;
}
trackers.Clear();
trackingLayer.RenderRequired = true;
UpdateMapControlSelection(fireSelectionChangedEvent);
}
private void SynchronizeTrackers()
{
trackers.Clear();
foreach (var trackerFeature in SelectedFeatureInteractors.SelectMany(featureInteractor => featureInteractor.Trackers))
{
trackers.Add(new LocalCoordinateSystemTrackerFeature(trackerFeature));
}
trackingLayer.RenderRequired = true;
}
///
/// Sets the selected object in the selectTool. SetSelection supports also the toggling/extending the
/// selected Trackers.
///
///
///
/// A clone of the original object.
/// special cases
/// feature is ILineString or IPolygon and trackerIndex != 1 : user clicked an already selected
/// features -> only selected tracker changes.
private void SetSelection(IFeature feature, ILayer featureLayer)
{
if (null != feature)
{
// store selected Trackers
IList featureTrackers = new List();
for (int i = 0; i < trackingLayer.DataSource.Features.Count; i++)
{
var trackerFeature = (TrackerFeature) trackingLayer.DataSource.Features[i];
if (ReferenceEquals(trackerFeature, feature))
{
featureTrackers.Add(i);
}
}
// store selected objects
AddSelection(featureLayer, feature);
}
}
private void FocusTracker(TrackerFeature trackFeature)
{
if (null == trackFeature)
{
return;
}
if (!((KeyToggleSelection) || (KeyExtendSelection)))
{
foreach (IFeatureInteractor featureInteractor in SelectedFeatureInteractors)
{
foreach (TrackerFeature trackerFeature in featureInteractor.Trackers)
{
featureInteractor.SetTrackerSelection(trackerFeature, false);
}
}
}
foreach (IFeatureInteractor featureInteractor in SelectedFeatureInteractors)
{
foreach (TrackerFeature trackerFeature in featureInteractor.Trackers)
{
if (trackerFeature == trackFeature)
{
if (KeyToggleSelection)
{
featureInteractor.SetTrackerSelection(trackFeature, !trackFeature.Selected);
}
else
{
featureInteractor.SetTrackerSelection(trackFeature, true);
}
}
}
}
}
private void UpdateMultiSelection(ICoordinate worldPosition)
{
if (MultiSelectionMode == MultiSelectionMode.Lasso)
{
selectPoints.Add(Map.WorldToImage(worldPosition));
}
else
{
WORLDPOSITION = worldPosition;
}
}
private static IPolygon CreatePolygon(double left, double top, double right, double bottom)
{
var vertices = new List
{
GeometryFactory.CreateCoordinate(left, bottom),
GeometryFactory.CreateCoordinate(right, bottom),
GeometryFactory.CreateCoordinate(right, top),
GeometryFactory.CreateCoordinate(left, top)
};
vertices.Add((ICoordinate) vertices[0].Clone());
ILinearRing newLinearRing = GeometryFactory.CreateLinearRing(vertices.ToArray());
return GeometryFactory.CreatePolygon(newLinearRing, null);
}
private IPolygon CreateSelectionPolygon(ICoordinate worldPosition)
{
if (MultiSelectionMode == MultiSelectionMode.Rectangle)
{
if (0 == Math.Abs(mouseDownLocation.X - worldPosition.X))
{
return null;
}
if (0 == Math.Abs(mouseDownLocation.Y - worldPosition.Y))
{
return null;
}
return CreatePolygon(Math.Min(mouseDownLocation.X, worldPosition.X),
Math.Max(mouseDownLocation.Y, worldPosition.Y),
Math.Max(mouseDownLocation.X, worldPosition.X),
Math.Min(mouseDownLocation.Y, worldPosition.Y));
}
var vertices = selectPoints.Select(point => Map.ImageToWorld(point)).ToList();
if (vertices.Count == 1)
{
// too few points to create a polygon
return null;
}
vertices.Add((ICoordinate) worldPosition.Clone());
vertices.Add((ICoordinate) vertices[0].Clone());
ILinearRing newLinearRing = GeometryFactory.CreateLinearRing(vertices.ToArray());
return GeometryFactory.CreatePolygon(newLinearRing, null);
}
private void UpdateMapControlSelection()
{
UpdateMapControlSelection(true);
}
private static ICoordinate TransformCoordinate(ICoordinate coordinate, IMathTransform mathTransform)
{
var transformCoordinate = mathTransform.Transform(new[]
{
coordinate.X,
coordinate.Y
});
return new Coordinate(transformCoordinate[0], transformCoordinate[1]);
}
private void UpdateMapControlSelection(bool fireSelectionChangedEvent)
{
SynchronizeTrackers();
IList selectedFeatures = SelectedFeatureInteractors.Select(t => t.SourceFeature).ToList();
MapControl.SelectedFeatures = selectedFeatures;
if (fireSelectionChangedEvent && SelectionChanged != null)
{
SelectionChanged(this, null);
}
}
private void HandleLayerStatusChanged(ILayer layer)
{
if (trackers.Any(trackerFeature => layer == trackerFeature.FeatureInteractor.Layer))
{
Clear();
}
}
}
}