ThinkGeo.com    |     Documentation    |     Premium Support

Unable to Union Split Polygons

Hi,


I have implemented a 'Split By Line' that allows the user to select one or more polygons in the map, then draw a line which splits any selected features it intersects.  Once the individual pieces have been split the user is presented with a dialog in which they can select the split pieces and assign them either back to the original feature, or create a new feature.  


The problem comes when I try to union the split pieces back together.  Adjancet pieces that should union together into a single polygon are instead unioned together as a two (or more) part polygon with  a line though them.


Here is Step 1, the user chooses to assign two pieces that were split apart to a New feature.


 


Step 2, note that the newly created feature is still divided by a line where it was split, even though I unioned the two pieces together.



 


The split was implemented using the GetDifference method.  Before GetDifference was called, the line drawn by the user was buffered using a very small number (0.00000001) for the buffer width.


How do I get those pieces to union (dissolve) together as a single polygon?


Thanks,


Steve



I found a solution to my problem that seems to be working OK for now.


I tried taking the intersection of the polygon used in the split process and unioning it back to each of the pieces that were split off.  This made the pieces very slightly larger at the edge where the split ocurred, apparently just enough so that when they're unioned back together they go to a one-part polygon instead of a two-part polygon. 


Since there is no method for splitting by a line this may be the best I can do.


 


 



Steven,


Thanks for your sharing on your ideas, I think it is great. I am sorry to say that I cannot see those example shots at the beginning of the thread.
 
Any more thoughts please feel comfortable to discuss.
 
Thanks.
 
Yale

Yale, 
  
 Sorry you couldn’t see the screenshots, I had to change the permission on those files to allow public access.  You should be able to see them now. 
  
 Thanks, 
  
 Steve

Steve,


Thanks for your posts and sharing, I appreciate it very much, I am not sure why I still cannot view the image screenshot posted at the beginning of the thread, while I do not think it is critical important.
 
Any more questions please feel free to let me know.
 
Thanks.
 
Yale

Steven, I'm a customer that had a similar issue.  A good line cutting routine would be a nice addition to MapSuite.   For your purposes of joining back together, your solution works great.    But your solution will not work if you were to save the polygons to a shapefile, and then let the user try to join them later.  You would have lost the original clipping buffer.  But what bothers me more, is that the saved polygon is not truly the original polygon that was split (well, one part of the polygon that was split).   It actually has a little missing area because of the width of your buffer... no matter how narrow.   Your resultant layer now has little tiny gaps between the polygons.   It's likely not an issue for your application.


For an academic discussion, I'll paste our solution.    This was taken off of a post I found on Google about solving this problem with JTS.   I converted it to use the free SQL Spatial Types library, but you could do the concept with NTS and MapSuite, I suspect.   Like you, the idea is that you must buffer the cutting line to make a narrow polygon and subtract it from the original polygon.  But, we immediately update the resultant multipolygon parts by comparing every vertex on the polygon part for being within tolerance of the original splitting line (tolerance being less than the buffer width).   If so, snap that vertex back to the splitting line's closest vertex.


This ensures that both polygon parts are using exactly the same vertex (from the cutting line), and they will union back together in your current session, or two months later when the user changes their mind  :)


 




        /// <summary>
        /// Cut a polygon into multiple parts with a cutting line.  The internal tolerance values for this
        /// routine are assuming a projected coordinate system with a linear unit in meters.
        /// </summary>
        /// <param name="ring">A polygon or multipolygon object that is to be split.</param>
        /// <param name="line">A cutting line.  This line must start outside of the ring,
        /// and end outside of the ring, but there is no test in this function to ensure that.
        /// The line can cross into and out of the ring any number of times.</param>
        /// <returns>The source geometry decomposed into a list of all polygons that were not intersected
        /// with the cutting line, and the parts of any polygon that lie on each side
        /// of the cutting line when the polygon is intersected by the cutting line.  Null
        /// is returned if there is no intersection of the cutting line with the source geometry.
        public static List<SqlGeometry> CutPolygonWithLine(SqlGeometry ring, SqlGeometry line)
        {
            // Clip the cutting line to the polygon boundary.  If there is no intersection
            // then just return.
            SqlGeometry clippedLine = line.STIntersection(ring);
            if (clippedLine.STIsEmpty())
                return null;

            // Create a polygon by buffering the cutting line by a token amount.
            SqlGeometry bufferedLine = line.BufferWithTolerance(.01, .1, false);
            
            // Split the polygon with the buffered line by subtracting the buffered line
            // from the polygon.  This ensures that the polygon gets turned into a multipolygon.
            SqlGeometry parts = ring.STDifference(bufferedLine);

            // Instantiate a collection of all of the parts of the multipolygon
            List<SqlGeometry> sides = new List<SqlGeometry>();

            // Iterate through the parts, and snap the vertices of each part that are close
            // to the original cutting line back to the actual vertices on the cutting line.
            // We are using the standard SqlGeometryBuilder construct to build a new geometry
            // from an existing geometry, with an operation to be performed on each component
            // of the original geometry during the build process.   The operation we perform
            // is snapping the point being copied to the nearest point on the origina line
            // if it is within tolerance.
            for (int idx = 1; idx <= parts.STNumGeometries(); idx++)
            {
                SqlGeometry part = parts.STGeometryN(idx);

                // Instantiate the standard SqlGeometry builder
                SqlGeometryBuilder builder = new SqlGeometryBuilder();

                // Create a snapping object, to build into the provided builder object.
                // This is the MapShots implementation of an object that the builder will call
                // to evaluate every point being copied from the source geometry relative to the
                // vertices of the clippedLine before copying the point to the new object.
                GeometrySnapper snapper = new GeometrySnapper(builder, clippedLine);
                
                // Execute the Populate method on the part, telling it to use our snapping object.
                part.Populate(snapper);

                // Add the newly constructed part to the list.
                sides.Add(builder.ConstructedGeometry);
            }

            return sides;
        }
    }


 



Hi Ted, 
  
 Thanks very much for sharing your solution, I will give it a try! 
  
 Steve

Ted & Steven, 
  
 Thanks for your sharing of experience and knowledge, I appreciate it very much. 
  
 Any more questions please feel free to let me know. 
  
 Thanks. 
  
 Yale