// adds feature to node public void add(Feature f) { // if node is leaf node and has less than 20 features if (!hasChildren && features.Count < 20) { features.Add(f); f.Parent = this; return; } // if node is leaf node and has more than 19 children if (!hasChildren) split(); // split node and divide features foreach (QuadTree qt in children) { // if child completely contains feature if (qt.gboundary.Contains(f.Geometry)) { qt.add(f); return; } } // if feature spans multiple children features.Add(f); f.Parent = this; }
private void readShape() { // read shape id, data length and shape type int id = br.ReadInt32BigEndian(); int length = br.ReadInt32BigEndian(); int stype = br.ReadInt32(); pos += 12; if (stype == 0) // Null return; Geometry g = null; if (stype == 1) // Point g = readPoint(); if (stype == 3) // Polyline g = readPolyline(); if (stype == 5) // Polygon g = readPolygon(); // add shape to layer if (g != null) { Feature f = new Feature(g, id); layer.addFeature(f); } }
// returns attributes for feature in layer public DataRow getRow(Feature f) { if (DataTable == null) return null; return DataTable.Rows[f.ID - 1]; }
// delete feature to layer public void delFeature(Feature s) { if (s.Selected) Selected.Remove(s); if (s.Parent != null) s.Parent.remove(s); Features.Remove(s.ID); }
// adds feature to layer public int addFeature(Feature s) { // checks for correct shapetype // a layer may only contain shapes of one type if (shapetype == ShapeType.EMPTY) { shapetype = convert(s.Geometry.GeometryType); if (shapetype == ShapeType.POINT) // default to black color for points Style.brush = new System.Drawing.SolidBrush(System.Drawing.Color.Black); } else if (shapetype != convert(s.Geometry.GeometryType)) throw new Exception("Wrong shapetype in layer " + name); // assigns id to feature if (s.ID > maxid) maxid = s.ID + 1; if (s.ID == -1) s.ID = maxid++; Features.Add(s.ID, s); if (QuadTree != null) QuadTree.add(s); return s.ID; }
// 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(); }
// calculate intersection of layers private void intersectButton_Click(object sender, EventArgs e) { toolBuilder.addHeader("Intersect"); // dropdown for selecting other layer ComboBox layerSelect = toolBuilder.addLayerSelect("Intersect with:"); // textbox for new layer name TextBox textbox = toolBuilder.addTextboxWithCaption("New layername:"); // laber for errors Label errorLabel = toolBuilder.addErrorLabel(); //button for performing intersection Button button = toolBuilder.addButton("Intersect", (Layer l) => { // new layer name not given if (textbox.Text.Length == 0) { toolBuilder.setError("Provide name"); return; } Layer intersectLayer = (Layer)layerSelect.SelectedItem; Layer newLayer = new Layer(textbox.Text); // if both layers have attributes if (l.DataTable != null && intersectLayer.DataTable != null) { // merge attributes, columnName collisions may be overwritten DataTable a = l.DataTable.Clone(); newLayer.DataTable = intersectLayer.DataTable.Clone(); newLayer.DataTable.Merge(a, true, MissingSchemaAction.Add); } // if only one layer has attributes else if (l.DataTable != null && intersectLayer.DataTable == null) { newLayer.DataTable = l.DataTable.Clone(); } // if only the other layer has attributes else if (l.DataTable == null && intersectLayer.DataTable != null) { newLayer.DataTable = intersectLayer.DataTable.Clone(); } // init progress bar progressLabel.Text = "Intersection"; progressBar.Minimum = 0; progressBar.Maximum = l.Features.Values.Count; // background worker for running in another thread BackgroundWorker bw = new BackgroundWorker(); bw.WorkerReportsProgress = true; bw.DoWork += (object wsender, DoWorkEventArgs we) => { // loop through all features for (int i = 0; i < l.Features.Count; i++ ) { Feature f = l.Features.Values.ElementAt(i); bw.ReportProgress(i); // get intersecting features var intersections = intersectLayer.getWithin(f.Geometry); foreach (Feature intersect in intersections) { DataRow arow = l.getRow(f); DataRow brow = intersectLayer.getRow(intersect); // calculate intersection Feature result = new Feature(f.Geometry.Intersection(intersect.Geometry)); int id = newLayer.addFeature(result); // merge attributes if (newLayer.DataTable != null) { DataRow dr = newLayer.DataTable.NewRow(); if (arow != null) foreach (DataColumn dc in arow.Table.Columns) dr[dc.ColumnName] = arow[dc.ColumnName]; if (brow != null) foreach (DataColumn dc in brow.Table.Columns) dr[dc.ColumnName] = brow[dc.ColumnName]; dr["sgis_id"] = id; newLayer.DataTable.Rows.Add(dr); } } } }; bw.RunWorkerCompleted += (object wsender, RunWorkerCompletedEventArgs we) => { // reset progress bar progressBar.Value = 0; progressLabel.Text = ""; // finalise new layer newLayer.calculateBoundingBox(); newLayer.createQuadTree(); Layers.Insert(0, newLayer); redraw(); }; bw.ProgressChanged += (object wsender, ProgressChangedEventArgs we) => { // update progress bar progressBar.Value = we.ProgressPercentage; }; bw.RunWorkerAsync(); }); // reset new layer name toolBuilder.resetAction += (Layer l) => { textbox.Text = (l == null) ? "" : l.Name + "_intersect"; }; toolBuilder.reset(); }
private void diffButton_Click(object sender, EventArgs e) { toolBuilder.addHeader("Difference"); // dropdown for selecting other layer ComboBox layerSelect = toolBuilder.addLayerSelect("Layer to subtract:"); // textbox for new layer name TextBox textbox = toolBuilder.addTextboxWithCaption("New layername:"); // label for errors Label errorLabel = toolBuilder.addErrorLabel(); // button for performing subtraction Button button = toolBuilder.addButton("Subtract", (Layer l) => { // no new layer name is given if (textbox.Text.Length == 0) { toolBuilder.setError("Provide name"); return; } // layers must have the same type of shapes Layer unionLayer = (Layer)layerSelect.SelectedItem; if (l.shapetype != unionLayer.shapetype) { toolBuilder.setError("Incompatible types"); return; } Layer newLayer = new Layer(textbox.Text); newLayer.DataTable = l.DataTable; // init progress bar progressLabel.Text = "Subtracting"; progressBar.Minimum = 0; progressBar.Maximum = l.Features.Values.Count; // background worker for running operation in another thread BackgroundWorker bw = new BackgroundWorker(); bw.WorkerReportsProgress = true; bw.DoWork += (object wsender, DoWorkEventArgs we) => { // threadsafe list for storing new features ConcurrentBag<Feature> newFeatures = new ConcurrentBag<Feature>(); using (var finished = new CountdownEvent(1)) { foreach (Feature f in l.Features.Values) { finished.AddCount(); Feature capt = f; // in each thread ThreadPool.QueueUserWorkItem((state) => { // clone feature Feature newf = new Feature((IGeometry)capt.Geometry.Clone(), capt.ID); // get intersecting features var intersects = unionLayer.getWithin(capt.Geometry); // subtract intersecting features foreach (Feature intersect in intersects) newf.Geometry = newf.Geometry.Difference(intersect.Geometry); // if there is something left of the original feature if (!newf.Geometry.IsEmpty) // add it to new feature list newFeatures.Add(newf); bw.ReportProgress(1); finished.Signal(); }, null); } finished.Signal(); finished.Wait(); } bw.ReportProgress(-newFeatures.Count); // add all processed features to new layer foreach (Feature f in newFeatures) { newLayer.addFeature(f); bw.ReportProgress(1); } }; bw.RunWorkerCompleted += (object wsender, RunWorkerCompletedEventArgs we) => { // reset progressbar progressBar.Value = 0; progressLabel.Text = ""; // create quad tree and insert new layer newLayer.calculateBoundingBox(); newLayer.createQuadTree(); 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 = "Creating spatial index"; } else progressBar.Value += we.ProgressPercentage; }; bw.RunWorkerAsync(); }); // reset new layer name when selected layer changes toolBuilder.resetAction += (Layer l) => { textbox.Text = (l == null) ? "" : l.Name + "_diff"; }; toolBuilder.reset(); }
public void remove(Feature f) { features.Remove(f); }