using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Drawing; using System.Linq; using DelftTools.Utils.Collections; using GeoAPI.Extensions.Feature; using GeoAPI.Geometries; using SharpMap.Api.Editors; using SharpMap.Api.Layers; using SharpMap.Converters.Geometries; using SharpMap.Data.Providers; using SharpMap.Editors; using SharpMap.Layers; using SharpMap.Rendering; using SharpMap.Rendering.Thematics; using SharpMap.Styles; namespace SharpMap.UI.Tools { /// /// ISnapper basic function for snapping to objects. Polygon or Line basesd object should make there /// own implementation /// public class SnapTool : MapTool { private readonly VectorLayer snapLayer = new VectorLayer(String.Empty); private readonly Collection geometries = new Collection(); private readonly Bitmap activeTracker; public SnapTool() { activeTracker = TrackerSymbolHelper.GenerateSimple(new Pen(Color.DarkGreen), new SolidBrush(Color.Orange), 8, 8); Failed = true; snapLayer.Name = "snapping"; var provider = new DataTableFeatureProvider(geometries); snapLayer.DataSource = provider; snapLayer.Style.Line = new Pen(Color.DarkViolet, 2); snapLayer.Style.Fill = new SolidBrush(Color.FromArgb(127, Color.DarkSlateBlue)); snapLayer.Style.Symbol = activeTracker; provider.Attributes.Columns.Add("id", typeof(string)); snapLayer.Theme = new CustomTheme(GetTrackerStyle); } public bool Failed { get; private set; } public SnapResult SnapResult { get; set; } public VectorLayer SnapLayer { get { return snapLayer; } } public void AddSnap(IGeometry geometry, ICoordinate from, ICoordinate to) { var vertices = new List { from, to }; var snapLineString = GeometryFactory.CreateLineString(vertices.ToArray()); snapLayer.DataSource.Add(snapLineString); } public void Reset() { SnapResult = null; Failed = true; snapLayer.DataSource.Features.Clear(); } /// /// Executes snapRule and shows the result in the snaptools' layer /// public SnapResult ExecuteSnapRule(ISnapRule snapRule, IFeature sourceFeature, IGeometry snapSource, IList snapTargets, ICoordinate worldPos, int trackingIndex) { var marge = (float) MapHelper.ImageToWorld(Map, snapRule.PixelGravity); var envelope = MapHelper.GetEnvelope(worldPos, marge); var snapCandidates = GetSnapCandidates(envelope); var candidates = snapCandidates as Tuple[] ?? snapCandidates.ToArray(); SnapResult = snapRule.Execute(sourceFeature, candidates, snapSource, snapTargets, worldPos, envelope, trackingIndex); ShowSnapResult(SnapResult); return SnapResult; } /// /// Update snapping /// /// /// The layer of feature. /// /// Feature that is snapped. Feature is not always available. /// /// actual geometry of the feature that is snapped. /// /// public SnapResult ExecuteLayerSnapRules(ILayer sourceLayer, IFeature feature, IGeometry geometry, ICoordinate worldPosition, int trackerIndex) { var snapRules = sourceLayer.FeatureEditor.SnapRules; SnapResult = null; for (int i = 0; i < snapRules.Count; i++) { ISnapRule rule = snapRules[i]; ExecuteSnapRule(rule, feature, geometry, null, worldPosition, trackerIndex); if (null != SnapResult) { break; } // If snapping failed for the last rule and snapping is obligatory // any position is valid // todo add rule with SnapRole.Free? if ((!rule.Obligatory) && (i == snapRules.Count - 1)) { SnapResult = new SnapResult(worldPosition, null, sourceLayer, null, -1, -1) { Rule = rule }; } } if (0 == snapRules.Count) { SnapResult = new SnapResult(worldPosition, null, sourceLayer, null, -1, -1); } return SnapResult; } public override void Render(Graphics graphics, Map mapBox) { snapLayer.OnRender(graphics, mapBox); } public override void Cancel() { Reset(); } private IEnumerable> GetSnapCandidates(IEnvelope envelope) { foreach (var layer in Map.GetAllVisibleLayers(true).Where(l => l.DataSource != null)) { foreach (var feature in layer.GetFeatures(envelope)) { yield return new Tuple(feature, layer); } } } private void ShowSnapResult(SnapResult snapResult) { var dataTableFeatureProvider = (DataTableFeatureProvider) snapLayer.DataSource; dataTableFeatureProvider.Clear(); if (snapResult == null) { return; } var visibleSnaps = snapResult.VisibleSnaps; if (visibleSnaps.Count == 0) { var vertices = CreateVertices(snapResult); if (vertices.Count > 1) { dataTableFeatureProvider.Add(GeometryFactory.CreateLineString(vertices.ToArray())); } dataTableFeatureProvider.Add(GeometryFactory.CreatePoint(snapResult.Location.X, snapResult.Location.Y)); } else { visibleSnaps.ForEach(s => dataTableFeatureProvider.Add(s)); } } private static List CreateVertices(SnapResult snapResult) { var vertices = new List(); if (snapResult.SnapIndexPrevious != -1) { vertices.Add((ICoordinate) snapResult.NearestTarget.Coordinates[snapResult.SnapIndexPrevious].Clone()); } vertices.Add((ICoordinate) snapResult.Location.Clone()); if (snapResult.SnapIndexNext != -1) { vertices.Add((ICoordinate) snapResult.NearestTarget.Coordinates[snapResult.SnapIndexNext].Clone()); } return vertices; } private VectorStyle GetTrackerStyle(IFeature feature) { var style = (VectorStyle) snapLayer.Style.Clone(); style.Symbol = activeTracker; return style; } } }