// perform union on currently selected layer private void unionButton_Click(object sender, EventArgs e) { toolBuilder.addHeader("Union"); // textbox for new layername TextBox textbox = toolBuilder.addTextboxWithCaption("New layername:"); Label errorLabel = toolBuilder.addErrorLabel(); Button button = toolBuilder.addButton("Union", (Layer l) => { // user has not set new layername if (textbox.Text.Length == 0) { toolBuilder.setError("Provide name"); return; } // create temporary layer Layer copyLayer = new Layer(l.Name); copyLayer.Boundingbox = new Envelope(l.Boundingbox); copyLayer.createQuadTree(); // copy all features to temp layer foreach (Feature f in l.Features.Values) copyLayer.addFeature(new Feature((IGeometry)f.Geometry.Clone(), f.ID)); // create new layer with same boundingbox Layer newLayer = new Layer(textbox.Text); newLayer.Boundingbox = new Envelope(l.Boundingbox); newLayer.createQuadTree(); // init progress bar int numFeatures = copyLayer.Features.Values.Count; progressLabel.Text = "Performing union"; progressBar.Minimum = 0; progressBar.Maximum = numFeatures; BackgroundWorker bw = new BackgroundWorker(); bw.WorkerReportsProgress = true; // perform merge in another thread bw.DoWork += (object wsender, DoWorkEventArgs we) => { // threadsafe list of merged features ConcurrentBag<Feature> newFeatures = new ConcurrentBag<Feature>(); var finished = new CountdownEvent(1); Object _lock = new object(); // create thread function var merge = new WaitCallback((state) => { Random rnd = new Random(); while (true) { Feature f; lock (_lock) { // break if no more features if (copyLayer.Features.Count == 0) break; // get random index int index = rnd.Next(copyLayer.Features.Count); // get corresponding random feature f = copyLayer.Features[copyLayer.Features.Keys.ToList()[index]]; // remove feature from layer copyLayer.delFeature(f); } f.ID = -1; while (true) { List<Feature> intersects; // aquire lock to avoid race conditions lock (_lock) { // get all features intersecting feature intersects = copyLayer.getWithin(f.Geometry); // remove features from layer foreach (Feature intersect in intersects) copyLayer.delFeature(intersect); } // if no intersects, no merging is necessary if (intersects.Count == 0) break; // merge all features foreach (Feature intersect in intersects) { f = new Feature(f.Geometry.Union(intersect.Geometry)); bw.ReportProgress(1); } } // add feature to list of new features newFeatures.Add(f); } finished.Signal(); }); // spawn eight threads, this is not always optimal but a good approximation for (int i = 0; i < 8; i++) { finished.AddCount(); ThreadPool.QueueUserWorkItem(merge); } finished.Signal(); finished.Wait(); bw.ReportProgress(-newFeatures.Count); // add all merged features back to temp layer foreach (Feature f in newFeatures) copyLayer.addFeature(f); newFeatures = new ConcurrentBag<Feature>(); finished = new CountdownEvent(1); // perform a final single threaded merge merge(false); // add all final merged features to new layer foreach (Feature f in newFeatures) newLayer.addFeature(f); }; bw.RunWorkerCompleted += (object wsender, RunWorkerCompletedEventArgs we) => { // reset progress bar progressBar.Value = 0; progressLabel.Text = ""; // insert new layer and redraw map Layers.Insert(0, newLayer); redraw(); }; bw.ProgressChanged += (object wsender, ProgressChangedEventArgs we) => { // update progress bar if (we.ProgressPercentage < 0) { progressBar.Value = 0; progressBar.Maximum = -we.ProgressPercentage; progressLabel.Text = "Union - Second pass"; } else progressBar.Value += we.ProgressPercentage; }; bw.RunWorkerAsync(); }); // reset default new layer name when selected layer is changed toolBuilder.resetAction = (Layer l) => { textbox.Text = (l == null) ? "" : l.Name + "_union"; }; toolBuilder.reset(); }