protected override async void OnClick() { SharedFunctions.Log("Starting To Pair Reservoirs"); DateTime startTime = DateTime.Now; await Project.Current.SaveEditsAsync(); try { await QueuedTask.Run(async() => { if (!SharedFunctions.LayerExists("DamCandidates") || !SharedFunctions.LayerExists("ReservoirSurfaces")) { return; } var damCandidatesLayer = MapView.Active.Map.FindLayers("DamCandidates").FirstOrDefault(); var reservoirSurfacesLayer = MapView.Active.Map.FindLayers("ReservoirSurfaces").FirstOrDefault(); SpatialReference = damCandidatesLayer.GetSpatialReference(); var reservoirPairsLayer = await CreateFeatureClass("ReservoirPairs"); await FindPairs(reservoirPairsLayer, reservoirSurfacesLayer as BasicFeatureLayer, damCandidatesLayer as BasicFeatureLayer); }); } catch (Exception ex) { MessageBox.Show(ex.ToString()); } finally { DateTime endTime = DateTime.Now; SharedFunctions.Log("Analysed in " + (endTime - startTime).TotalSeconds.ToString() + " seconds"); } }
public async static Task <BasicFeatureLayer> CreateDamFeatureClass(string name) { var existingLayer = MapView.Active.Map.FindLayers(name).FirstOrDefault(); if (existingLayer != null) { return(existingLayer as BasicFeatureLayer); } SharedFunctions.Log("Creating DamCandidates layer"); List <object> arguments = new List <object> { CoreModule.CurrentProject.DefaultGeodatabasePath, name, "POLYLINE", "", "DISABLED", "ENABLED" }; arguments.Add(SpatialReference); IGPResult result = await Geoprocessing.ExecuteToolAsync("CreateFeatureclass_management", Geoprocessing.MakeValueArray(arguments.ToArray())); var layer = MapView.Active.Map.FindLayers(name).FirstOrDefault() as BasicFeatureLayer; await SharedFunctions.ExecuteAddFieldTool(layer, "ContourID", "LONG"); await SharedFunctions.ExecuteAddFieldTool(layer, "StartPointID", "LONG"); await SharedFunctions.ExecuteAddFieldTool(layer, "EndPointID", "LONG"); await SharedFunctions.ExecuteAddFieldTool(layer, "ContourHeight", "SHORT"); await SharedFunctions.ExecuteAddFieldTool(layer, "LengthRating", "FLOAT"); await SharedFunctions.ExecuteAddFieldTool(layer, "DistanceOnLine", "LONG"); await SharedFunctions.ExecuteAddFieldTool(layer, "Length", "SHORT"); await SharedFunctions.ExecuteAddFieldTool(layer, "StartPointDistance", "LONG"); await SharedFunctions.ExecuteAddFieldTool(layer, "EndPointDistance", "LONG"); await SharedFunctions.ExecuteAddFieldTool(layer, "DamHeight", "SHORT"); await SharedFunctions.ExecuteAddFieldTool(layer, "DamVolume", "LONG"); await SharedFunctions.ExecuteAddFieldTool(layer, "ReservoirVolume", "LONG"); await SharedFunctions.ExecuteAddFieldTool(layer, "VolumeRating", "FLOAT"); await SharedFunctions.ExecuteAddFieldTool(layer, "DamSpansContourStart", "SHORT"); return(layer); }
private static void AnalyseContourHeights(Dictionary <int, SortedDictionary <int, SortedDictionary <long, MapPoint> > > contourHeights, CancellationToken ctoken) { int selectedCandidates = 0; foreach (var contourHeight in contourHeights) { //Analyse lines of one ContourLine foreach (var contourID in contourHeight.Value) { //skip contours with less than 2.000 m length if (contourID.Value.Count < (int)2000 / pointsIntervalOnContour) { continue; } List <CandidateDam> candidates = AnalyseCountourPoints(contourID.Value, contourHeight.Key, contourID.Key, ctoken); lock (lockingObject) { chosenCandidates.AddRange(candidates); selectedCandidates += candidates.Count; } } SharedFunctions.Log(selectedCandidates.ToString("N0") + " candidates selected for " + contourHeight.Key + "m contours (Total: " + chosenCandidates.Count.ToString("N0") + " of " + PotentialCandidates.ToString("N0") + " potentials)"); } }
protected override async void OnClick() { SharedFunctions.Log("Starting To Create 3D Visualization"); DateTime startTime = DateTime.Now; CIMLineSymbol symbol = SymbolFactory.Instance.ConstructLineSymbol(ColorFactory.Instance.GreyRGB, 5.0, SimpleLineStyle.Solid); CIMSymbolReference symbolReference = symbol.MakeSymbolReference(); try { await QueuedTask.Run(async() => { if (!SharedFunctions.LayerExists("DamCandidates") || !SharedFunctions.LayerExists("ReservoirSurfaces")) { return; } var damCandidatesLayer = MapView.Active.Map.FindLayers("DamCandidates").FirstOrDefault(); var reservoirSurfacesLayer = MapView.Active.Map.FindLayers("ReservoirSurfaces").FirstOrDefault(); SpatialReference = damCandidatesLayer.GetSpatialReference(); var damVisLayer = await CreateMultiPatchFeatureClass("DamVisualization"); var reservoirVisLayer = await CreatePolygonFeatureClass("ReservoirVisualization"); Visualize(damVisLayer, reservoirVisLayer, reservoirSurfacesLayer as BasicFeatureLayer, damCandidatesLayer as BasicFeatureLayer); }); } catch (Exception ex) { MessageBox.Show(ex.ToString()); } finally { DateTime endTime = DateTime.Now; SharedFunctions.Log("Visualized in " + (endTime - startTime).TotalSeconds.ToString() + " seconds"); } }
protected override async void OnClick() { SharedFunctions.Log("Search for candidate dams started"); pointsIntervalOnContour = Convert.ToInt32(Parameter.PointIntervalBox.Text); DateTime startTime = DateTime.Now; chosenCandidates = new List <CandidateDam>(); PotentialCandidates = 0; var pd = new ProgressDialog("Search for candidate dams", "Canceled", 100, false); cps = new CancelableProgressorSource(pd); cps.Progressor.Max = 100; PointsAnalyzed = 0; TotalPointsCount = 0; try { await Project.Current.SaveEditsAsync(); BasicFeatureLayer layer = null; await QueuedTask.Run(async() => { if (!SharedFunctions.LayerExists("ContourPoints")) { return; } CancellationToken ctoken = new CancellationToken(); //create line feature layer if it does not exist BasicFeatureLayer damCandidatesLayer = await CreateDamFeatureClass("DamCandidates"); var contourPointsLayer = MapView.Active.Map.FindLayers("ContourPoints").FirstOrDefault(); layer = contourPointsLayer as BasicFeatureLayer; // store the spatial reference of the current layer SpatialReference = layer.GetSpatialReference(); //Cursor for selected points RowCursor cursor = layer.GetSelection().Search(); //If no selection was set, use full points layer if (layer.GetSelection().GetCount() == 0) { cursor = layer.Search(); } Dictionary <int, SortedDictionary <int, SortedDictionary <long, MapPoint> > > contourHeights = new Dictionary <int, SortedDictionary <int, SortedDictionary <long, MapPoint> > >(); cps.Progressor.Status = "Loading ContourPoints into memory"; SharedFunctions.Log("Loading all ContourPoints into memory"); while (cursor.MoveNext()) { if (ctoken.IsCancellationRequested) { SharedFunctions.Log("Canceled"); return; } using (Row row = cursor.Current) { var point = row[1] as MapPoint; var pointID = (int)row[0]; var contourHeight = (int)(double)row[4]; var contourID = (int)row[2]; if (!ContourLengths.ContainsKey(contourID)) { ContourLengths.Add(contourID, (double)row["Shape_Length"]); } if (!contourHeights.ContainsKey((int)contourHeight)) { contourHeights.Add((int)contourHeight, new SortedDictionary <int, SortedDictionary <long, MapPoint> >()); } if (!contourHeights[contourHeight].ContainsKey((int)contourID)) { contourHeights[contourHeight].Add((int)contourID, new SortedDictionary <long, MapPoint>()); } contourHeights[contourHeight][(int)contourID].Add(pointID, point); TotalPointsCount++; } } cps.Progressor.Status = "Analyze Contours"; SharedFunctions.Log("Analyze Contours"); bool multiThreading = (Parameter.MultiThreadingBox == null || !Parameter.MultiThreadingBox.IsChecked.HasValue || Parameter.MultiThreadingBox.IsChecked.Value); if (multiThreading) { HeightsToProcess = contourHeights.Keys.ToList(); int ThreadCount = Math.Min(HeightsToProcess.Count, Environment.ProcessorCount); SharedFunctions.Log("Divided work into " + ThreadCount + " threads..."); await Task.WhenAll(Enumerable.Range(1, ThreadCount).Select(c => Task.Run( () => { while (HeightsToProcess.Count > 0) // && !ctoken.IsCancellationRequested) { int height = -1; lock (HeightsToProcess) { height = HeightsToProcess.FirstOrDefault(); if (height != 0) { HeightsToProcess.Remove(height); } } if (height != -1) { var calc = new Dictionary <int, SortedDictionary <int, SortedDictionary <long, MapPoint> > >(); calc.Add(height, contourHeights[height]); AnalyseContourHeights(calc, ctoken); } } } , ctoken)) ); } else { //Single Thread: AnalyseContourHeights(contourHeights, ctoken); } cps.Progressor.Status = "Saving all " + chosenCandidates.Count + " candidates"; SharedFunctions.Log("Saving all " + chosenCandidates.Count + " candidates"); foreach (var candidate in chosenCandidates) { if (ctoken.IsCancellationRequested) { SharedFunctions.Log("Canceled"); return; } //Create coordinates for Polyline Feature with height ContourHeight + 5 Meters! List <Coordinate3D> coordinates = new List <Coordinate3D>() { new Coordinate3D(candidate.StartPoint.X, candidate.StartPoint.Y, candidate.ContourHeight + 5), new Coordinate3D(candidate.EndPoint.X, candidate.EndPoint.Y, candidate.ContourHeight + 5) }; //save all selected candidates into the db var attributes = new Dictionary <string, object> { { "Shape", PolylineBuilder.CreatePolyline(coordinates) }, { "ContourID", (long)candidate.ContourID }, { "StartPointID", (long)candidate.StartPointID }, { "EndPointID", (long)candidate.EndPointID }, { "ContourHeight", (short)candidate.ContourHeight }, { "LengthRating", (float)candidate.Rating }, { "DistanceOnLine", (long)candidate.DistanceOnLine }, { "Length", (short)candidate.Length }, { "StartPointDistance", (long)candidate.StartPointDistance }, { "EndPointDistance", (long)candidate.EndPointDistance }, { "DamSpansContourStart", (short)(candidate.DamSpansContourStart ? 1 : 0) } }; var editOp = new EditOperation() { Name = "Create dam candidate", SelectNewFeatures = false }; editOp.Create(damCandidatesLayer, attributes); ////Execute the operations editOp.Execute(); } }, cps.Progressor); //save all edits await Project.Current.SaveEditsAsync(); } catch (Exception ex) { MessageBox.Show(ex.ToString()); } finally { DateTime endTime = DateTime.Now; SharedFunctions.Log("Analysed " + PotentialCandidates.ToString("N0") + " candidates ( " + chosenCandidates.Count.ToString("N0") + " selected) in " + (endTime - startTime).TotalSeconds.ToString("N") + " seconds"); } }
private async static void Visualize(BasicFeatureLayer dam3dLayer, BasicFeatureLayer reservoirVisLayer, BasicFeatureLayer reservoirSurfacesLayer, BasicFeatureLayer damLayer) { SharedFunctions.DeleteAllFeatures(reservoirVisLayer); SharedFunctions.DeleteAllFeatures(dam3dLayer); List <long> damIDs = new List <long>(); var reservoirPairsLayer = MapView.Active.Map.FindLayers("ReservoirPairs").FirstOrDefault(); if (reservoirPairsLayer != null && ((BasicFeatureLayer)reservoirPairsLayer).SelectionCount > 0) { var reservoirPairsCursor = ((BasicFeatureLayer)reservoirPairsLayer).GetSelection().Search(); while (reservoirPairsCursor.MoveNext()) { using (Row row = reservoirPairsCursor.Current) { int damId = (int)row["LowerDamId"]; if (!damIDs.Contains(damId)) { damIDs.Add(damId); } damId = (int)row["UpperDamId"]; if (!damIDs.Contains(damId)) { damIDs.Add(damId); } } } } if (damLayer.SelectionCount > 0) { var damCursor = damLayer.GetSelection().Search(); while (damCursor.MoveNext()) { using (Row row = damCursor.Current) { int damId = (int)row["ObjectID"]; if (!damIDs.Contains(damId)) { damIDs.Add(damId); } } } } List <CandidateDam> candidates = new List <CandidateDam>(); SharedFunctions.LoadDamCandidatesFromLayer(candidates, damLayer); foreach (var dam in candidates.Where(c => damIDs.Contains(c.ObjectID)).ToList()) { double contourHeight = dam.ContourHeight; try { MapPoint startPoint = dam.StartPoint; MapPoint endPoint = dam.EndPoint; double damHeight = (double)dam.DamHeight; double damLength = (double)dam.Length; Coordinate3D coord1 = startPoint.Coordinate3D; int factor = ((startPoint.Y <endPoint.Y && startPoint.X> endPoint.X) || (startPoint.Y > endPoint.Y && startPoint.X < endPoint.X) ) ? 1 : -1; Coordinate3D coord2 = new Coordinate3D(coord1.X + factor * damHeight / damLength * Math.Abs(startPoint.Y - endPoint.Y) //X , coord1.Y + damHeight / damLength * Math.Abs(startPoint.X - endPoint.X) //Y , coord1.Z - damHeight); //Z Coordinate3D coord3 = new Coordinate3D(coord1.X - factor * damHeight / damLength * Math.Abs(startPoint.Y - endPoint.Y) //X , coord1.Y - damHeight / damLength * Math.Abs(startPoint.X - endPoint.X) //Y , coord1.Z - damHeight); //Z //Workaround for Bug in ArcGIS Pro 2.4.1: if values are equal, extrusion will fail coord2.X += 0.1; coord2.Y += 0.1; coord3.X += 0.1; coord3.Y += 0.1; List <Coordinate3D> coords = new List <Coordinate3D>(); coords.Add(coord1); coords.Add(coord2); coords.Add(coord3); var newPolygon3D = PolygonBuilder.CreatePolygon(coords, SpatialReference); Coordinate3D coord = new Coordinate3D(endPoint.X - startPoint.X, endPoint.Y - startPoint.Y, 0.1); var multipatch = GeometryEngine.Instance.ConstructMultipatchExtrudeAlongVector3D(newPolygon3D, coord); var attributes2 = new Dictionary <string, object> { { "Shape", multipatch }, { "DamID", (long)dam.ObjectID } }; var createOperation2 = new EditOperation() { Name = "Create multipatch", SelectNewFeatures = false }; createOperation2.Create(dam3dLayer, attributes2); await createOperation2.ExecuteAsync(); //add SurfacePolygon to Visualization: var queryFilter = new QueryFilter { WhereClause = string.Format("DamID = {0}", dam.ObjectID) }; var surfaceCursor = reservoirSurfacesLayer.Select(queryFilter).Search(); if (surfaceCursor.MoveNext()) { using (Row row = surfaceCursor.Current) { var polygon = (row as Feature).GetShape() as Polygon; attributes2 = new Dictionary <string, object> { { "Shape", polygon }, { "DamID", (long)dam.ObjectID } }; var createOperationSurface = new EditOperation() { Name = "Create surface", SelectNewFeatures = false }; createOperationSurface.Create(reservoirVisLayer, attributes2); await createOperationSurface.ExecuteAsync(); } } } catch (Exception ex) { SharedFunctions.Log("Error for 3D Dam with DamID " + dam.ObjectID + ": " + ex.Message); } SharedFunctions.Log("3D Dam created for Dam " + dam.ObjectID); } await Project.Current.SaveEditsAsync(); }
protected override async void OnClick() { SharedFunctions.Log("DamVolume Calculation started"); DateTime startTime = DateTime.Now; await Project.Current.SaveEditsAsync(); IncomingCandidates = 0; OutgoingCandidates = 0; List <CandidateDam> candidates = new List <CandidateDam>(); List <CandidateDam> candidates2Delete = new List <CandidateDam>(); try { await QueuedTask.Run(async() => { if (!SharedFunctions.LayerExists("DamCandidates") || !SharedFunctions.LayerExists("Contours")) { return; } var _damCandidatesLayer = MapView.Active.Map.FindLayers("DamCandidates").FirstOrDefault(); BasicFeatureLayer damCandidatesLayer = _damCandidatesLayer as BasicFeatureLayer; SharedFunctions.LoadDamCandidatesFromLayer(candidates, damCandidatesLayer); IncomingCandidates = candidates.Count(); OutgoingCandidates = IncomingCandidates; //create stacked profile string inputDEM = Parameter.DEMCombo.SelectedItem.ToString(); var valueArray = Geoprocessing.MakeValueArray(damCandidatesLayer.Name, inputDEM, "DamProfile", null); var environments = Geoprocessing.MakeEnvironmentArray(overwriteoutput: true); var cts = new CancellationTokenSource(); await Project.Current.SaveEditsAsync(); var res = await Geoprocessing.ExecuteToolAsync("StackProfile", valueArray, environments, cts.Token, null, GPExecuteToolFlags.Default); //analyse profile and add data to line feature or delete line feature //profile can be used to calculate frontal dam area and dam volume?! var damProfileTable = MapView.Active.Map.FindStandaloneTables("DamProfile").FirstOrDefault(); if (damProfileTable == null) { SharedFunctions.Log("No DamProfile Table found!"); } CandidateDam cand = null; double prev_dist = 0; int prev_lineID = -1; using (var profileCursor = damProfileTable.Search()) { while (profileCursor.MoveNext()) { using (Row profileRow = profileCursor.Current) { var first_dist = (double)profileRow[1]; var first_z = (double)profileRow[2]; var line_ID = (int)profileRow[5]; if (prev_lineID != line_ID) { prev_dist = 0; cand = candidates.SingleOrDefault(c => c.ObjectID == line_ID); // set Volume and ZMin to the default values cand.DamVolume = 0; cand.ZMin = cand.ContourHeight; } else if (candidates2Delete.Contains(cand)) { continue; } //set lowest point of dam to calculate max dam height later if (cand.ZMin > (int)first_z) { cand.ZMin = (int)first_z; } if ((int)first_z > (cand.ContourHeight + 5)) { candidates2Delete.Add(cand); continue; } //add volume of current block of dam... the assumption is a triangle dam shape (cross section) with alpha = 45° //thus the area of the cross section is calculated by <height²> and the volume by <height² * length> cand.DamVolume += (long)(Math.Pow((cand.ContourHeight - first_z), 2) * (first_dist - prev_dist)); prev_lineID = line_ID; prev_dist = first_dist; } } } await DeleteCandidates(candidates, damCandidatesLayer, candidates2Delete); //remove candidates with dam heights outside of the limits await DeleteCandidates(candidates, damCandidatesLayer, candidates.Where(c => (c.ContourHeight - c.ZMin) < 10 || (c.ContourHeight - c.ZMin) > 300).ToList()); //update the new attributes to the feature foreach (var candidate in candidates) { var editOp2 = new EditOperation(); Dictionary <string, object> attributes = new Dictionary <string, object>(); attributes.Add("DamHeight", (short)(candidate.ContourHeight - candidate.ZMin)); attributes.Add("DamVolume", (long)(candidate.DamVolume)); editOp2.Modify(damCandidatesLayer, candidate.ObjectID, attributes); var resu = await editOp2.ExecuteAsync(); } await Project.Current.SaveEditsAsync(); }); } catch (Exception ex) { MessageBox.Show(ex.ToString()); } finally { DateTime endTime = DateTime.Now; SharedFunctions.Log("Analysed in " + (endTime - startTime).TotalSeconds.ToString("N") + " seconds completed (" + OutgoingCandidates.ToString("N0") + " of " + IncomingCandidates.ToString("N0") + " candidates left)"); } }
private async static Task <bool> FindPairs(BasicFeatureLayer reservoirPairsLayer, BasicFeatureLayer reservoirSurfacesLayer, BasicFeatureLayer damLayer) { List <ReservoirPair> reservoirPairs = new List <ReservoirPair>(); List <CandidateDam> res1List = new List <CandidateDam>(); SharedFunctions.LoadDamCandidatesFromLayer(res1List, damLayer); List <ReservoirSurface> reservoirSurfaceList = new List <ReservoirSurface>(); SharedFunctions.LoadReservoirSurfacesFromLayer(reservoirSurfaceList, reservoirSurfacesLayer); List <CandidateDam> res2List = new List <CandidateDam>(res1List); int analyzedCounter = 0; int createdCounter = 0; foreach (var dam1 in res1List) { res2List.Remove(dam1); foreach (var dam2 in res2List) { try { analyzedCounter++; CandidateDam lowerDam = null; CandidateDam upperDam = null; if (dam1.ContourHeight > dam2.ContourHeight) { lowerDam = dam2; upperDam = dam1; } else { lowerDam = dam1; upperDam = dam2; } //check for height-difference: int usableHeightDifference = upperDam.ContourHeight - upperDam.DamHeight - lowerDam.ContourHeight; if (usableHeightDifference < 50) { continue; } //check for horizontal distance Coordinate3D lowerDamCenter = new Coordinate3D((lowerDam.StartPoint.X + lowerDam.EndPoint.X) / 2, (lowerDam.StartPoint.Y + lowerDam.EndPoint.Y) / 2, (lowerDam.StartPoint.Z + lowerDam.EndPoint.Z) / 2); Coordinate3D upperDamCenter = new Coordinate3D((upperDam.StartPoint.X + upperDam.EndPoint.X) / 2, (upperDam.StartPoint.Y + upperDam.EndPoint.Y) / 2, (upperDam.StartPoint.Z + upperDam.EndPoint.Z) / 2); decimal distanceBetweenDamCenters = SharedFunctions.DistanceBetween(lowerDamCenter, upperDamCenter); if (distanceBetweenDamCenters > 5000) { continue; } //check for reservoir overlap //NOT NECCESSARY due to height-difference check, where all corresponding cases are already filtered //if (GeometryEngine.Instance.Overlaps(reservoirSurfaceList.Single(c => c.DamID == dam1.ObjectID).Polygon, reservoirSurfaceList.Single(c => c.DamID == dam1.ObjectID).Polygon)) // continue; //calculate CapacityInMWh: long usableVolume = upperDam.ReservoirVolume; if (lowerDam.ReservoirVolume < usableVolume) { usableVolume = lowerDam.ReservoirVolume; } //only assume 85% of the water as usable in reality usableVolume = (long)(0.85 * usableVolume); float capacityInMWh = (float)(1000 * 9.8 * usableHeightDifference * usableVolume * 0.9) / (float)((long)3600 * 1000000); //calculate utilizationPercentage: float capacityUtilization = 0; if (upperDam.ReservoirVolume != 0) { capacityUtilization = (float)lowerDam.ReservoirVolume / upperDam.ReservoirVolume; } if (capacityUtilization > 1) { capacityUtilization = 1 / (float)capacityUtilization; } //check for Utilization of at least 75% if (capacityUtilization < 0.75) { continue; } decimal capacityDistanceRatio = (decimal)capacityInMWh * 100 / distanceBetweenDamCenters; List <Coordinate3D> coordinates = new List <Coordinate3D>() { lowerDamCenter, upperDamCenter }; Polyline polyline = PolylineBuilder.CreatePolyline(coordinates); ReservoirPair reservoirPair = new ReservoirPair(); reservoirPair.LowerDam = lowerDam; reservoirPair.UpperDam = upperDam; reservoirPair.LowerDamCenter = lowerDamCenter; reservoirPair.UpperDamCenter = upperDamCenter; reservoirPair.Polyline = polyline; reservoirPair.LowerDamID = lowerDam.ObjectID; reservoirPair.UpperDamID = upperDam.ObjectID; reservoirPair.CapacityInMWh = capacityInMWh; reservoirPair.Distance = distanceBetweenDamCenters; reservoirPair.LowerHeight = lowerDam.ContourHeight; reservoirPair.UpperHeight = upperDam.ContourHeight; reservoirPair.CapacityDistanceRatio = capacityDistanceRatio; reservoirPair.UsableHeightDifference = usableHeightDifference; reservoirPair.CapacityUtilization = capacityUtilization; reservoirPairs.Add(reservoirPair); } catch (Exception ex) { SharedFunctions.Log("Error for attempted Pair with dam1: " + dam1.ObjectID + " and dam2: " + dam2.ObjectID + " (" + ex.Message + ")"); } } } //try to further minimize the reservoirPairs selection by only keeping //the best connection within a buffer of 100 m (around both dam center points) List <ReservoirPair> pairsToDelete = new List <ReservoirPair>(); foreach (var reservoirPair in reservoirPairs.ToList()) { if (pairsToDelete.Contains(reservoirPair)) { continue; } var similarPairs = reservoirPairs.Where(c => SharedFunctions.DistanceBetween(reservoirPair.LowerDamCenter, c.LowerDamCenter) <= 100 && SharedFunctions.DistanceBetween(reservoirPair.UpperDamCenter, c.UpperDamCenter) <= 100).ToList(); if (similarPairs.Count > 1) { var bestPair = similarPairs.OrderByDescending(c => c.CapacityDistanceRatio * (decimal)c.CapacityUtilization).First(); similarPairs.Remove(bestPair); pairsToDelete = pairsToDelete.Union(similarPairs).ToList(); } } foreach (var pairToDelete in pairsToDelete) { reservoirPairs.Remove(pairToDelete); } //insert the remaining objects into the DB foreach (var reservoirPair in reservoirPairs) { var attributes = new Dictionary <string, object> { { "Shape", reservoirPair.Polyline }, { "LowerDamID", (long)reservoirPair.LowerDamID }, { "UpperDamID", (long)reservoirPair.UpperDamID }, { "CapacityInMWh", (float)reservoirPair.CapacityInMWh }, { "Distance", (long)reservoirPair.Distance }, { "LowerHeight", (short)reservoirPair.LowerHeight }, { "UpperHeight", (short)reservoirPair.UpperHeight }, { "CapacityDistanceRatio", (float)reservoirPair.CapacityDistanceRatio }, { "UsableHeightDifference", (short)reservoirPair.UsableHeightDifference }, { "CapacityUtilization", (float)reservoirPair.CapacityUtilization } }; var createOperation = new EditOperation() { Name = "Create reservoir pair", SelectNewFeatures = false }; createOperation.Create(reservoirPairsLayer, attributes); await createOperation.ExecuteAsync(); createdCounter++; } SharedFunctions.Log(analyzedCounter + " combinations analysed and " + createdCounter + " viable pairs found"); await Project.Current.SaveEditsAsync(); return(true); }
private async static void ConstructPolygons() { List <CandidateDam> candidates = new List <CandidateDam>(); SharedFunctions.LoadDamCandidatesFromLayer(candidates, damLayer); List <Contour> contours = new List <Contour>(); SharedFunctions.LoadContoursFromLayer(contours, contourLayer); //select only contours, that actually have candidate dams on it contours = contours.Where(c => candidates.Any(d => d.ContourID == c.ObjectID)).ToList(); bool multiThreading = (Parameter.MultiThreadingBox == null || !Parameter.MultiThreadingBox.IsChecked.HasValue || Parameter.MultiThreadingBox.IsChecked.Value); //ArcGIS.Core.Geometry Tools currently don't seem to support multithreading. //Question https://community.esri.com/thread/243147-multithreading-parallel-processing-in-arcgis-pro-addin //received no answer so far. Until a solution is found, multithreading logic has to be deactivated multiThreading = false; if (multiThreading) { List <PolylineBuilder> polylineBuilders = new List <PolylineBuilder>(); for (int i = 0; i < Environment.ProcessorCount; i++) { polylineBuilders.Add(new PolylineBuilder(SpatialReference)); } ContoursToProcess = contours.Select(c => c.ObjectID).ToList(); SharedFunctions.Log("Divided work into " + Environment.ProcessorCount + " threads for all logical Processors..."); await Task.WhenAll(polylineBuilders.Select(c => Task.Run(//Enumerable.Range(1, Environment.ProcessorCount).Select(c => Task.Run( async() => { while (ContoursToProcess.Count > 0) { long contourID = -1; lock (ContoursToProcess) { contourID = ContoursToProcess.FirstOrDefault(); if (contourID != 0) { ContoursToProcess.Remove(contourID); } } if (contourID != -1) { var calc = new List <Contour>() { contours.Single(d => d.ObjectID == contourID) }; await PolygonsForContours(candidates, calc, c); } } } )) ); } else { await PolygonsForContours(candidates, contours, new PolylineBuilder(SpatialReference)); } SharedFunctions.Log("Save all " + surfaces.Count + " surfaces"); foreach (var surface in surfaces) { var attributes = new Dictionary <string, object> { { "Shape", surface.Polygon }, { "DamID", (long)surface.DamID }, { "ContourHeight", (short)surface.ContourHeight } }; var createOperation = new EditOperation() { Name = "Create reservoir polygon", SelectNewFeatures = false }; createOperation.Create(reservoirSurfacesLayer, attributes); await createOperation.ExecuteAsync(); } await Project.Current.SaveEditsAsync(); }
private static async Task PolygonsForContours(List <CandidateDam> candidates, List <Contour> contours, PolylineBuilder polylineBuilder) { foreach (var contour in contours) { var contourGeometry = contour.Polyline; int counter = 0; int contourHeight = 0; foreach (var candidate in candidates.Where(c => c.ContourID == contour.ObjectID).ToList()) { try { while (polylineBuilder.CountParts > 0) { polylineBuilder.RemovePart(0); } //add the full contour polylineBuilder.AddParts(contourGeometry.Parts); //split at the endpoint polylineBuilder.SplitAtDistance(candidate.EndPointDistance, false, true); if (candidate.DamSpansContourStart) { //remove the part of the contour after the endpoint //split at the startpoint if (candidate.StartPointDistance != 0) { polylineBuilder.SplitAtDistance(candidate.StartPointDistance, false, true); //remove the part of the polyline before the startpoint polylineBuilder.RemovePart(1); } //Handle the situation, when the startpoint is on the very beginning of the contour line else { polylineBuilder.RemovePart(0); } } else { //remove the part of the contour after the endpoint polylineBuilder.RemovePart(1); //split at the startpoint if (candidate.StartPointDistance != 0) { polylineBuilder.SplitAtDistance(candidate.StartPointDistance, false, true); //remove the part of the polyline before the startpoint polylineBuilder.RemovePart(0); } } var newPolygon3D = PolygonBuilder.CreatePolygon(polylineBuilder.ToGeometry().Copy3DCoordinatesToList(), SpatialReference); ReservoirSurface surface = new ReservoirSurface(); surface.Polygon = GeometryEngine.Instance.Move(newPolygon3D, 0, 0, candidate.ContourHeight) as Polygon; surface.DamID = (long)candidate.ObjectID; surface.ContourHeight = (short)candidate.ContourHeight; surfaces.Add(surface); counter++; contourHeight = candidate.ContourHeight; } catch (Exception ex) { SharedFunctions.Log("Error for DamID " + candidate.ObjectID + " for ContourHeight " + candidate.ContourHeight + ": (" + ex.Message + ")"); } } SharedFunctions.Log("Created " + surfaces.Count.ToString("N0") + " Polygons ... " + counter.ToString("N0") + " for Contour " + contour.ObjectID + " (" + contour.Height + " m)"); } }