protected override void LogDerivedGeometriesCalculated(CancelableProgressor progressor) { if (_overlaps != null && _overlaps.Notifications.Count > 0) { _msg.Info(_overlaps.Notifications.Concatenate(Environment.NewLine)); if (!_overlaps.HasOverlaps()) { _msg.InfoFormat("Select one or more different features."); } } else if (_overlaps == null || !_overlaps.HasOverlaps()) { _msg.Info( "No overlap of other polygons with current selection found. Select one or more different features."); } if (_overlaps != null && _overlaps.HasOverlaps()) { string msg = _overlaps.OverlapGeometries.Count == 1 ? "Select the overlap to subtract from the selection" : "Select one or more overlaps to subtract from the selection. Draw a box to select overlaps completely within the box."; _msg.InfoFormat(LocalizableStrings.RemoveOverlapsTool_AfterSelection, msg); } }
protected override void AfterSelection(IList <Feature> selectedFeatures, CancelableProgressor progressor) { CalculateDerivedGeometries(selectedFeatures, progressor); DerivedGeometriesCalculated(progressor); }
protected override async Task <bool> OnSketchCompleteAsync(Geometry sketchGeometry) { _msg.VerboseDebug("OnSketchCompleteAsync"); if (sketchGeometry == null) { // TODO: if in selection phase select at the current mouse location? return(false); } try { CancelableProgressor progressor = GetCancelableProgressor(); SketchingMoveType = GetSketchingMoveType(sketchGeometry); if (RequiresSelection && IsInSelectionPhase()) { return(await OnSelectionSketchComplete(sketchGeometry, progressor)); } return(await OnSketchCompleteCoreAsync(sketchGeometry, progressor)); } catch (Exception e) { HandleError($"{Caption}: Error completing sketch ({e.Message})", e); // NOTE: Throwing here results in a process crash (Exception while waiting for a Task to complete) // Consider Task.FromException? } return(false); }
protected override Task OnSelectionChangedAsync(MapSelectionChangedEventArgs e) { // NOTE: This method is not called when the selection is cleared by another command (e.g. by 'Clear Selection') // Is there another way to get the global selection changed event? What if we need the selection changed in a button? //if (_shiftIsPressed) // always false -> toolkeyup is first. This method is apparently scheduled to run after key up //{ // return Task.FromResult(true); //} CancelableProgressor progressor = GetOverlapsCalculationProgressor(); if (IsInSelectionPhase()) { var selectedFeatures = MapUtils.GetFeatures(e.Selection).ToList(); if (CanUseSelection(selectedFeatures)) { AfterSelection(selectedFeatures, progressor); var sketch = GetCurrentSketchAsync().Result; SelectAndProcessDerivedGeometry(e.Selection, sketch, progressor); } } return(Task.FromResult(true)); }
private IList <Feature> GetOverlappingFeatures( [NotNull] ICollection <Feature> selectedFeatures, [CanBeNull] CancelableProgressor cancellabelProgressor) { Dictionary <MapMember, List <long> > selection = ActiveMapView.Map.GetSelection(); Envelope inExtent = ActiveMapView.Extent; TargetFeatureSelection targetFeatureSelection = TargetFeatureSelection.VisibleFeatures; IEnumerable <KeyValuePair <FeatureClass, List <Feature> > > foundOidsByClass = MapUtils.FindFeatures(ActiveMapView, selection, targetFeatureSelection, CanOverlapLayer, inExtent, cancellabelProgressor); if (cancellabelProgressor != null && !cancellabelProgressor.CancellationToken.IsCancellationRequested) { return(new List <Feature>()); } var foundFeatures = new List <Feature>(); foreach (var keyValuePair in foundOidsByClass) { foundFeatures.AddRange(keyValuePair.Value); } // Remove the selected features from the set of overlapping features. // This is also important to make sure the geometries don't get mixed up / reset // by inserting target vertices foundFeatures.RemoveAll( f => selectedFeatures.Any(s => GdbObjectUtils.IsSameFeature(f, s))); return(foundFeatures); }
private Overlaps CalculateOverlaps(IList <Feature> selectedFeatures, IList <Feature> overlappingFeatures, CancelableProgressor progressor) { Overlaps overlaps = null; CancellationToken cancellationToken; if (progressor != null) { cancellationToken = progressor.CancellationToken; } else { var cancellationTokenSource = new CancellationTokenSource(); cancellationToken = cancellationTokenSource.Token; } if (MicroserviceClient != null) { overlaps = MicroserviceClient.CalculateOverlaps(selectedFeatures, overlappingFeatures, cancellationToken); } else { throw new InvalidConfigurationException("Microservice has not been started."); } return(overlaps); }
protected override void CalculateDerivedGeometries(IList <Feature> selectedFeatures, CancelableProgressor progressor) { selectedFeatures = GetApplicableSelectedFeatures(selectedFeatures).ToList(); IList <Feature> overlappingFeatures = GetOverlappingFeatures(selectedFeatures, progressor); if (progressor != null && !progressor.CancellationToken.IsCancellationRequested) { _msg.Warn("Calculation of removable overlaps was cancelled."); return; } _overlaps = CalculateOverlaps(selectedFeatures, overlappingFeatures, progressor); if (progressor != null && progressor.CancellationToken.IsCancellationRequested) { _msg.Warn("Calculation of removable overlaps was cancelled."); return; } // TODO: Options bool insertVerticesInTarget = false; _overlappingFeatures = insertVerticesInTarget ? overlappingFeatures : null; _feedback.Update(_overlaps); }
private ChangeAlongCurves CalculateReshapeCurves( [NotNull] IList <Feature> selectedFeatures, [NotNull] IList <Feature> targetFeatures, [CanBeNull] CancelableProgressor progressor) { ChangeAlongCurves result; CancellationToken cancellationToken; if (progressor != null) { cancellationToken = progressor.CancellationToken; } else { var cancellationTokenSource = new CancellationTokenSource(); cancellationToken = cancellationTokenSource.Token; } if (MicroserviceClient != null) { result = CalculateChangeAlongCurves(selectedFeatures, targetFeatures, cancellationToken); result.TargetFeatures = targetFeatures; } else { throw new InvalidConfigurationException("Microservice has not been started."); } return(result); }
protected override async Task <bool> OnSketchCompleteCoreAsync( Geometry sketchGeometry, CancelableProgressor progressor) { var result = await QueuedTask.Run(async() => { List <Feature> selection = GetApplicableSelectedFeatures(ActiveMapView).ToList(); if (!IsInSubcurveSelectionPhase()) { // 2. Phase: target selection: return(SelectTargets(selection, sketchGeometry, progressor)); } // 3. Phase: reshape/cut line selection: List <CutSubcurve> cutSubcurves = GetSelectedCutSubcurves(sketchGeometry); if (cutSubcurves.Count > 0) { return(await UpdateFeatures(selection, cutSubcurves, progressor)); } // No subcurve hit, try target selection instead return(SelectTargets(selection, sketchGeometry, progressor)); }); return(result); }
private void ProcessSelection([NotNull] MapView activeMapView, [CanBeNull] CancelableProgressor progressor = null) { Dictionary <MapMember, List <long> > selectionByLayer = activeMapView.Map.GetSelection(); NotificationCollection notifications = new NotificationCollection(); List <Feature> applicableSelection = GetApplicableSelectedFeatures(selectionByLayer, notifications).ToList(); int selectionCount = selectionByLayer.Sum(kvp => kvp.Value.Count); if (applicableSelection.Count > 0 && (AllowNotApplicableFeaturesInSelection || applicableSelection.Count == selectionCount)) { LogUsingCurrentSelection(); AfterSelection(applicableSelection, progressor); } else { if (selectionCount > 0) { _msg.InfoFormat(notifications.Concatenate(Environment.NewLine)); } LogPromptForSelection(); StartSelectionPhase(); } }
public static IEnumerable <KeyValuePair <FeatureClass, List <Feature> > > FindFeatures( [NotNull] MapView mapView, [NotNull] Dictionary <MapMember, List <long> > intersectingSelectedFeatures, TargetFeatureSelection targetSelectionType, [CanBeNull] Predicate <FeatureLayer> layerPredicate = null, [CanBeNull] Envelope extent = null, [CanBeNull] CancelableProgressor cancelableProgressor = null) { Assert.ArgumentCondition( targetSelectionType != TargetFeatureSelection.SelectedFeatures && targetSelectionType != TargetFeatureSelection.Undefined, "Unsupported target selection type"); var selectedFeatures = GetFeatures(intersectingSelectedFeatures).ToList(); var searchGeometry = GetSearchGeometry(selectedFeatures, extent); if (searchGeometry == null) { yield break; } foreach (var keyValuePair in FindFeatures( mapView, searchGeometry, targetSelectionType, layerPredicate, selectedFeatures, cancelableProgressor)) { yield return(keyValuePair); } }
public static IEnumerable <Feature> GetFeatures( [NotNull] Table featureClass, [CanBeNull] QueryFilter filter, bool recycling, [CanBeNull] CancelableProgressor cancelableProgressor = null) { var cursor = featureClass.Search(filter, recycling); try { while (cursor.MoveNext()) { if (cancelableProgressor != null && cancelableProgressor.CancellationToken.IsCancellationRequested) { yield break; } var feature = (Feature)cursor.Current; yield return(feature); } } finally { cursor.Dispose(); } }
protected override void AfterSelection(IList <Feature> selectedFeatures, CancelableProgressor progressor) { if (CanStartSketchPhase(selectedFeatures)) { StartSketchPhase(); } }
/// <summary> /// Finds the features in the map by the specified criteria, grouped by feature class /// </summary> /// <param name="mapView">The map view containing the layers to search</param> /// <param name="searchGeometry">The search geometry</param> /// <param name="spatialRelationship">The spatial relationship between the found features /// and the search geometry.</param> /// <param name="targetSelectionType">The target selection type that determines which layers /// are searched.</param> /// <param name="layerPredicate">An extra layer predicate that allows for a more /// fine-granular determination of the layers to be searched.</param> /// <param name="featurePredicate">An extra feature predicate that allows to determine /// criteria on the feature level.</param> /// <param name="selectedFeatures">The selected features, relevant only for /// <see cref="targetSelectionType"/> with value <see cref="TargetFeatureSelection.SameClass"/>. </param> /// <param name="cancelableProgressor"></param> /// <returns></returns> public static IEnumerable <KeyValuePair <FeatureClass, List <Feature> > > FindFeatures( [NotNull] MapView mapView, [NotNull] ArcGIS.Core.Geometry.Geometry searchGeometry, SpatialRelationship spatialRelationship, TargetFeatureSelection targetSelectionType, [CanBeNull] Predicate <FeatureLayer> layerPredicate, [CanBeNull] Predicate <Feature> featurePredicate, List <Feature> selectedFeatures, CancelableProgressor cancelableProgressor = null) { // NOTE: FeatureLayer.Search is quite useless, as we cannot control recyclability and as soon as the cursor // is disposed, the feature's geometry is wrong! // -> Get the distinct feature classes (TODO: include layer definition queries) IEnumerable <FeatureLayer> featureLayers = GetLayers <FeatureLayer>( mapView, fl => IsLayerApplicable(fl, targetSelectionType, layerPredicate, selectedFeatures)); IEnumerable <IGrouping <IntPtr, FeatureLayer> > layersGroupedByClass = featureLayers.GroupBy(fl => fl.GetFeatureClass().Handle); foreach (var layersInClass in layersGroupedByClass) { // One query per distinct definition query, then make OIDs distinct FeatureClass featureClass = null; List <Feature> features = new List <Feature>(); foreach (IGrouping <string, FeatureLayer> layers in layersInClass.GroupBy( fl => fl.DefinitionQuery)) { if (cancelableProgressor != null && cancelableProgressor.CancellationToken.IsCancellationRequested) { yield break; } featureClass = layers.First().GetFeatureClass(); QueryFilter filter = GdbQueryUtils.CreateSpatialFilter(searchGeometry, spatialRelationship); filter.WhereClause = layers.Key; IEnumerable <Feature> foundFeatures = GdbQueryUtils .GetFeatures(featureClass, filter, false) .Where(f => featurePredicate == null || featurePredicate(f)); features.AddRange(foundFeatures); } if (featureClass != null && features.Count > 0) { yield return(new KeyValuePair <FeatureClass, List <Feature> >( featureClass, features.DistinctBy(f => f.GetObjectID()).ToList())); } } }
//protected override void OnKeyUpCore(MapViewKeyEventArgs k) //{ // _msg.VerboseDebug("OnKeyUpCore"); // if (k.Key == _keyToggleNonDefaultSide) // { // _nonDefaultSideMode = ! _nonDefaultSideMode; // k.Handled = true; // } // else if (k.Key == Key.Space) // { // k.Handled = true; // } // base.OnKeyUpCore(k); //} //protected override async Task HandleKeyUpAsync(MapViewKeyEventArgs k) //{ // // At 2.5 this is never called (despite setting k.Handled = true above). // TODO: Test in 2.6/2.7 // try // { // if (k.Key == _keyToggleNonDefaultSide || // k.Key == Key.Space) // { // _updateFeedbackTask = UpdateFeedbackAsync(_nonDefaultSideMode); // await _updateFeedbackTask; // } // } // catch (Exception e) // { // _msg.Warn("Error generating preview", e); // } // finally // { // _updateFeedbackTask = null; // } //} protected override async Task <bool> OnEditSketchCompleteCoreAsync( Geometry sketchGeometry, EditingTemplate editTemplate, MapView activeView, CancelableProgressor cancelableProgressor = null) { _feedback.Clear(); // TODO: cancel all running background tasks... var polyline = (Polyline)sketchGeometry; List <Feature> selection; // Or allow selecting next feature already? SetCursor(Cursors.Wait); bool success = await QueuedTaskUtils.Run(async() => { selection = GetApplicableSelectedFeatures(activeView).ToList(); var potentiallyAffectedFeatures = GetAdjacentFeatures(selection, cancelableProgressor); // This timout should be enough even in extreme circumstances: int timeout = selection.Count * 10000; _cancellationTokenSource = new CancellationTokenSource(timeout); ReshapeResult result = MicroserviceClient.Reshape( selection, polyline, potentiallyAffectedFeatures, true, true, _nonDefaultSideMode, _cancellationTokenSource.Token); if (result == null) { return(false); } Dictionary <Feature, Geometry> resultFeatures = result.ResultFeatures.ToDictionary(r => r.Feature, r => r.UpdatedGeometry); LogReshapeResults(result, selection.Count); success = await SaveAsync(resultFeatures); // At some point, hopefully, read-only operations on the CIM model can run in parallel await ToolUtils.FlashResultPolygonsAsync(activeView, resultFeatures); return(success); }); _nonDefaultSideMode = false; //if (!_advancedReshapeOptions.RemainInSketchMode) { StartSelectionPhase(); } return(success); // taskSave.Result; }
protected override void AfterSelection(IList <Feature> selectedFeatures, CancelableProgressor progressor) { WorkListsModule.Current.OnWorkItemPicked(new WorkItemPickArgs { features = selectedFeatures.ToList() }); SelectionUtils.ClearSelection(ActiveMapView.Map); }
protected override CancelableProgressor GetSelectionProgressor() { var selectionCompleteProgressorSource = new CancelableProgressorSource( "Selecting features bla bla...", "cancelled", true); CancelableProgressor selectionProgressor = selectionCompleteProgressorSource.Progressor; return(selectionProgressor); }
protected CancelableProgressor GetOverlapsCalculationProgressor() { var overlapsCalculationProgressorSource = new CancelableProgressorSource( "Calculating overlaps...", "cancelled", true); CancelableProgressor selectionProgressor = overlapsCalculationProgressorSource.Progressor; return(selectionProgressor); }
public static Task <T> Run <T>([NotNull] Func <Task <T> > function, [CanBeNull] CancelableProgressor progressor = null, TaskCreationOptions creationOptions = TaskCreationOptions.None) { // NOTE on the standard QueuedTask.Run: if the progressor is null, there's an argument exception. Task <T> result = progressor == null ? QueuedTask.Run(function, creationOptions) : QueuedTask.Run(function, progressor, creationOptions); return(result); }
protected override async Task <bool> OnSketchCompleteCoreAsync( Geometry sketchGeometry, CancelableProgressor progressor) { var result = await QueuedTask.Run(() => { var selection = ActiveMapView.Map.GetSelection(); return(SelectAndProcessDerivedGeometry(selection, sketchGeometry, progressor)); }); return(result); }
protected override async Task <bool> OnSketchCompleteCoreAsync( Geometry sketchGeometry, CancelableProgressor progressor) { if (IsInSketchMode) { // take snapshots EditingTemplate currentTemplate = CurrentTemplate; MapView activeView = ActiveMapView; return(await OnEditSketchCompleteCoreAsync( sketchGeometry, currentTemplate, activeView, progressor)); } return(false); }
private async Task <bool> UpdateFeatures(List <Feature> selectedFeatures, List <CutSubcurve> cutSubcurves, CancelableProgressor progressor) { CancellationToken cancellationToken = progressor?.CancellationToken ?? new CancellationTokenSource().Token; ChangeAlongCurves newChangeAlongCurves; IList <Feature> targetFeatures = Assert.NotNull(_changeAlongCurves.TargetFeatures); List <ResultFeature> updatedFeatures = ChangeFeaturesAlong( selectedFeatures, targetFeatures, cutSubcurves, cancellationToken, out newChangeAlongCurves); _changeAlongCurves = newChangeAlongCurves; _feedback.Update(_changeAlongCurves); HashSet <long> editableClassHandles = MapUtils.GetLayers <BasicFeatureLayer>(MapView.Active, bfl => bfl.IsEditable) .Select(l => l.GetTable().Handle.ToInt64()).ToHashSet(); // Updates: Dictionary <Feature, Geometry> resultFeatures = updatedFeatures .Where(f => GdbPersistenceUtils.CanChange( f, editableClassHandles, RowChangeType.Update)) .ToDictionary(r => r.Feature, r => r.NewGeometry); // Inserts (in case of cut), grouped by original feature: Dictionary <Feature, IList <Geometry> > insertsByOriginal = updatedFeatures .Where(f => GdbPersistenceUtils.CanChange( f, editableClassHandles, RowChangeType.Insert)) .GroupBy(f => f.Feature, f => f.NewGeometry) .ToDictionary(g => g.Key, g => (IList <Geometry>)g.ToList()); // TODO //LogReshapeResults(result, selection.Count); var success = await GdbPersistenceUtils.SaveInOperationAsync( EditOperationDescription, resultFeatures, insertsByOriginal); return(success); }
private void RefreshCutSubcurves([NotNull] IList <Feature> selectedFeatures, [CanBeNull] CancelableProgressor progressor = null) { if (_changeAlongCurves == null || _changeAlongCurves.TargetFeatures == null || _changeAlongCurves.TargetFeatures.Count == 0) { return; } ChangeAlongCurves newState = CalculateReshapeCurves(selectedFeatures, _changeAlongCurves.TargetFeatures, progressor); _changeAlongCurves.Update(newState); _feedback.Update(_changeAlongCurves); }
private void DerivedGeometriesCalculated([CanBeNull] CancelableProgressor progressor) { if (progressor == null || !progressor.CancellationToken.IsCancellationRequested) { _msg.DebugFormat("{0}: Derived geometries calculated.", Caption); } else { _msg.DebugFormat("{0}: Derived geometry calculation was cancelled.", Caption); } if (CanUseDerivedGeometries()) { StartSecondPhase(); } LogDerivedGeometriesCalculated(progressor); }
private bool SelectTargets(List <Feature> selectedFeatures, Geometry sketch, CancelableProgressor progressor) { const TargetFeatureSelection targetFeatureSelection = TargetFeatureSelection.VisibleFeatures; sketch = ToolUtils.SketchToSearchGeometry(sketch, GetSelectionTolerancePixels(), out bool _); Predicate <Feature> canUseAsTargetFeature = t => CanUseAsTargetFeature(selectedFeatures, t); SpatialRelationship spatialRel = SketchType == SketchGeometryType.Polygon ? SpatialRelationship.Contains : SpatialRelationship.Intersects; var foundOidsByLayer = MapUtils.FindFeatures(ActiveMapView, sketch, spatialRel, targetFeatureSelection, CanUseAsTargetLayer, canUseAsTargetFeature, selectedFeatures, progressor); // TODO: Picker if single click and several found if (progressor != null && progressor.CancellationToken.IsCancellationRequested) { _msg.Warn("Calculation of reshape lines was cancelled."); return(false); } IList <Feature> allTargetFeatures = GetDistinctSelectedFeatures(foundOidsByLayer, _changeAlongCurves?.TargetFeatures, KeyboardUtils.IsModifierPressed(Keys.Shift)); _changeAlongCurves = allTargetFeatures.Count > 0 ? CalculateReshapeCurves(selectedFeatures, allTargetFeatures, progressor) : new ChangeAlongCurves(new List <CutSubcurve>(), ReshapeAlongCurveUsability.NoTarget); _feedback.Update(_changeAlongCurves); return(true); }
private void ProcessSelection([NotNull] IEnumerable <Feature> selectedFeatures, [CanBeNull] CancelableProgressor progressor = null) { // TODO: currently the selection is retrieved twice. Testing the selection should // in the success case return it so that it can be passed to AfterSelection // BUT: some genius tools require the selection to be grouped by layer IList <Feature> selection = selectedFeatures.ToList(); if (!CanUseSelection(selection)) { LogPromptForSelection(); StartSelectionPhase(); return; } LogUsingCurrentSelection(); AfterSelection(selection, progressor); }
protected override bool SelectAndProcessDerivedGeometry( Dictionary <MapMember, List <long> > selection, Geometry sketch, CancelableProgressor progressor) { Assert.NotNull(_overlaps); Overlaps overlapsToRemove = SelectOverlaps(_overlaps, sketch); if (!overlapsToRemove.HasOverlaps()) { return(false); } IEnumerable <Feature> selectedFeatures = MapUtils.GetFeatures(selection); RemoveOverlapsResult result = MicroserviceClient.RemoveOverlaps( selectedFeatures, overlapsToRemove, _overlappingFeatures, progressor?.CancellationToken ?? new CancellationTokenSource().Token); var updates = new Dictionary <Feature, Geometry>(); var inserts = new Dictionary <Feature, IList <Geometry> >(); foreach (var resultPerFeature in result.ResultsByFeature) { updates.Add(resultPerFeature.OriginalFeature, resultPerFeature.UpdatedGeometry); if (resultPerFeature.InsertGeometries.Count > 0) { inserts.Add(resultPerFeature.OriginalFeature, resultPerFeature.InsertGeometries); } } bool saved = GdbPersistenceUtils.SaveInOperation("Remove overlaps", updates, inserts); var currentSelection = SelectionUtils.GetSelectedFeatures(MapView.Active).ToList(); CalculateDerivedGeometries(currentSelection, progressor); return(saved); }
protected override async Task <bool> OnEditSketchCompleteCoreAsync( Geometry sketchGeometry, EditingTemplate editTemplate, MapView activeView, CancelableProgressor cancelableProgressor = null) { var polygon = (Polygon)sketchGeometry; var resultFeatures = await QueuedTaskUtils.Run( () => CalculateResultFeatures(activeView, polygon), cancelableProgressor); var taskSave = QueuedTaskUtils.Run(() => SaveAsync(resultFeatures)); var taskFlash = QueuedTaskUtils.Run(() => FlashAsync(activeView, resultFeatures)); await Task.WhenAll(taskFlash, taskSave); return(taskSave.Result); }
private IList <Feature> GetAdjacentFeatures( [NotNull] ICollection <Feature> selectedFeatures, [CanBeNull] CancelableProgressor cancellabelProgressor) { Dictionary <MapMember, List <long> > selection = ActiveMapView.Map.GetSelection(); if (!selection.Keys.Any(mm => mm is FeatureLayer fl && fl.ShapeType == esriGeometryType.esriGeometryPolyline)) { return(null); } Envelope inExtent = ActiveMapView.Extent; // TODO: Use linear network classes as defined in reshape options TargetFeatureSelection targetFeatureSelection = TargetFeatureSelection.SameClass; IEnumerable <KeyValuePair <FeatureClass, List <Feature> > > foundOidsByClass = MapUtils.FindFeatures( ActiveMapView, selection, targetFeatureSelection, layer => layer.ShapeType == esriGeometryType.esriGeometryPolyline, inExtent, cancellabelProgressor); if (cancellabelProgressor != null && !cancellabelProgressor.CancellationToken.IsCancellationRequested) { return(new List <Feature>()); } var foundFeatures = new List <Feature>(); foreach (var keyValuePair in foundOidsByClass) { foundFeatures.AddRange(keyValuePair.Value); } foundFeatures.RemoveAll( f => selectedFeatures.Any(s => GdbObjectUtils.IsSameFeature(f, s))); return(foundFeatures); }
private void DerivedGeometriesCalculated([CanBeNull] CancelableProgressor progressor) { if (progressor == null || !progressor.CancellationToken.IsCancellationRequested) { _msg.DebugFormat("{0}: Derived geometries calculated.", Caption); } else { _msg.DebugFormat("{0}: Derived geometry calculation was cancelled.", Caption); } if (CanUseDerivedGeometries()) { StartSecondPhase(); } else { // In case it has not yet been started (e.g. on tool activation with selection) StartSelectionPhase(); } LogDerivedGeometriesCalculated(progressor); }