2

Maybe you can help me solve a problem regarding topology cleanup that I recently came a cross.

I have two layers with bunch of polygons (or feature classes as I'm in the ESRI world) that should share a common boundary. (ex. water body polygon and vegetation coverage - the shore line is the common boundary).

Usually Integrate in ArcMap would be the tool for the job, but in this case I'm only allowed to modify one of the layers, the other one is the reference layer and it must not be touched. In other words I must snap polygons in one layer to the polygons of the other at the common boundary.

Here's and illustration of the problem:

The defective border has some extra nodes (marked in blue). If I use Integrate those blue nodes will be introduced to the reference polygon layer regardless of ranks set. So in this case I need a tool that will make the boundary identical by removing nodes from the polygons of the defective layer.

As a side note. The rest of the nodes on the shared boundary are snapped together, just the blue ones mess my border.

Any advice?

I have ArcInfo 10.0 and ET Geowizard available and probably can work with OSGIS soft, but would prefer to avoid converting my layers to shp.

  • Are the blue vertices co-linear in the line of their predecessor and successor? If yes can you remove them? – huckfinn Jan 29 '14 at 20:49
  • Yes, they are collinear within the default tolerance (0.001m). Just like a line where you add a vertex with edge snapping on. Can I remove them? As in am I allowed to? Well yeah, that's the goal. – pg85 Jan 30 '14 at 7:44
1

You can use the "simplify" with DP point removal algorithm ( with a small tolerance ) to remove those points that are nearly colinear, then you can integrate to make sure that your topology is correct.

  • Thanks radouxju. This seems to be to appropriate solution. I'm going to do FeatureVerticesToPoints as Roland suggests on the before and after FC, to keep an eye on what really got removed. – pg85 Jan 30 '14 at 8:14
  • I have confirmed that this actually solves my problem. Stackexchange community has done it again :) – pg85 Jan 30 '14 at 11:24
2

I like @radouxju's idea, but I'm not sure you could control for which points get removed.

Another, longer-winded approach could be to use FeatureVerticesToPoints on both FCs, add a "flag" field to the veg FC points, use SelectLayerByLocation to set those veg FC points also found in the water body FC to "1" or something.

Then you could select the unflagged points and use that list to delete the corresponding vertices from the original veg FC. Not exactly sure how to go from here. Could use arcpy.da.UpdateCursor, along the lines of something like:

with arcpy.da.UpdateCursor('vegFC', ["SHAPE@"]) as cursor:
    for row in cursor:
        arrayPts = row[0].getPart()
        # do some stuff

There's also another approach here.

Not sure about programmatically removing vertices. Might have to remove the problem vertices from arrayPts and do something like PointsToLine. Probably need to monkey around w/arrayPts before using PointsToLine. There might be a more direct way to do this with editing. I don't do much interactive stuff.

1

I try to make a sketch how to remove co-linear points at boundaries, but wondering a little bit about the fact that ArcMap has problems with that matter. Normally ArcInfo the predecessor of ArcGis uses the commands generate and clean, to decompose a polygon cover into cover based on arcs, where the polygonal structure is build by a network of arcs. The areal identity of each polygon is reperesented by the orientation of the arc (left/right side) and an assigment of a polygon ID for each side, called ArcID. So the name "ArcInfo" is a tribute to these technic/method.

OK hanging and co-linear nodes can removed by a triangular contemplation of a vertex triple and the calculation of the enclosing area or the angle. Sufficent condition for a hanging node is, that this area is nearly zero or the enclosing angle is near -0,+0, or -180/180 degree and the sign of the area is given by the sequence and orientation of the triangle shape (necessary condition).

enter image description here

An example program in java a (transcript into python will be simple I think):


The Vertex Class in Vertex.java

    /** 2D Vertex service class */
    public class Vertex {

        /** x co-coordinate */
        public double x;

        /** y co-coordinate */
        public double y;

        public Vertex(double aX, double aY) {
            x = aX; y = aY;
        }

        /** Square of the euclidian distance by scalar params */
        public static double distanceSquared(
                double X1, double Y1, 
                double X2, double Y2
                ) {
            double X = X2-X1; X = X * X;
            double Y = Y2-Y1; Y = Y * Y;
            return (X + Y );
        }

        /** Square of the euclidian distance by vertex params */
        public static double distanceSquared(Vertex A, Vertex B) {
            return distanceSquared(A.x, A.y, B.x, B.y);
        }

        /** The routine calculates the angle of a vertex triple 
         *  between (frist,center,last) in radiant. The result is 
         *  Double.NaN the three vertices are at the same location in 
         *  one of the cases:
         * (frist=center||last==center||first=center=middle). 
         */
        public static double tripleAngle(Vertex first, Vertex center, Vertex last) {
            double tr = tripleArea(first, center, last);
            double ac = scalarProduct(first,center, last);
            if (Double.isNaN(ac)) return Double.NaN;
            double acx = Math.acos(ac);
            double acg = acx;
            if (tr>=0) return acg; else if (tr<0) return -acg;
            return 0;
        }

        /** The routine calculates the triangle area of the 
         *  vertex triple (first , center, last). If the orientation
         *  is counter clockwise (ccw) the area is negative and positive
         *  if it is clockwise (cw).
         */
        public static double tripleArea(Vertex first, Vertex center, Vertex last) {
            return      
              (first .y  + center.y)  /2 * (center.x - first.x  )
            + (center.y  + last.y)    /2 * (last.x   - center.x )
            + (first .y  + last.y)    /2 * (first.x  - last.x   )  ;
        }

        /** The routine calculates the scalar product of a vertex triple 
         *  (first , center, last) where center is the origin if the angle.
         *  The result is Double.NaN the three vertices are at the 
         *  same location in one of the cases
         * (frist=center||last==center||first=center=middle). 
         *  liegen wird Double.NaN zurueckgegeben.*/
         public static double scalarProduct(Vertex first, Vertex center, Vertex last) {
            double a = distanceSquared(first,center);
            double aRoot = Math.sqrt(a);
            double b = distanceSquared(center,last);
            double bRoot = Math.sqrt(b);
            double c = distanceSquared(last,first);
            if (a*b==0) return Double.NaN;
            else return (c-a-b)/-(2*aRoot*bRoot);
        }

    }

The VertexVector Class in VertexVector.java

    import java.util.ArrayList;

    /** 2D VertexVector service class */
    public class VertexVector extends ArrayList<Vertex> {

        static final long serialVersionUID = 1000L;

       void removeHangingNodes(double epsilonArea) {
           int i = 1; 
           if (size()<3) return;
           while ( i<size()-1) {
               Vertex first  = get(i-1);
               Vertex center = get(i);
               Vertex last   = get(i+1);
               double area = Vertex.tripleArea(first, center, last);
               if (Math.abs(area) < epsilonArea) {
                   System.out.println("remove id="+i+" x="+get(i).x+" y="+get(i).y+" a="+area);
                   remove(i); 
               }
               i++;
           }
       }

       /** List the vertex vector */
       void list() {
           for (int i=0; i<size(); i++) {
               System.out.println("id="+i+" x="+get(i).x+" y="+get(i).y); 
           }
       }
    }

The Test Programm in in VertexTest.java

    /** Test class for the main program */
    public class VertexTest {

        /** Test the removement of hanging nodes */
        public static void main(String argv[]) {

            VertexVector vec = new VertexVector();
            System.out.println("--- BEFORE ---");
            vec.add(new Vertex(0,0));
            vec.add(new Vertex(1,1)); //co-linear
            vec.add(new Vertex(2,2)); 
            vec.add(new Vertex(3,2));
            vec.add(new Vertex(3,3)); 
            vec.add(new Vertex(2,2)); //co-linear
            vec.add(new Vertex(1,1)); //co-linear
            vec.add(new Vertex(0,0));
            vec.list();
            System.out.println("--- REMOVE ---");
            vec.removeHangingNodes(0.0001);
            System.out.println("--- AFTER ---");
            vec.list();
        }
    }

The Input/Output

    --- BEFORE ---
    id=0 x=0.0 y=0.0
    id=1 x=1.0 y=1.0
    id=2 x=2.0 y=2.0  <-- co-linear vertex
    id=3 x=3.0 y=2.0
    id=4 x=3.0 y=3.0
    id=5 x=2.0 y=2.0  <-- co-linear vertex
    id=6 x=1.0 y=1.0  <-- co-linear vertex
    id=7 x=0.0 y=0.0
    --- REMOVE ---
    remove id=1 x=1.0 y=1.0 a=0.0
    remove id=4 x=2.0 y=2.0 a=0.0
    --- AFTER ---
    id=0 x=0.0 y=0.0
    id=1 x=2.0 y=2.0
    id=2 x=3.0 y=2.0
    id=3 x=3.0 y=3.0
    id=4 x=1.0 y=1.0 <-- co-linear vertex left by stepping
    id=5 x=0.0 y=0.0

As you see, one remove cycle will be not enough. Tis is because of the stepping. If you do'nt use the stepping (i++) you will remove co-linear leading points and not co-linear vertices points between.

How to compile with gcj

           gcj -I $(pwd) --main=VertexTest \
               -o VertexTest \
                  Vertex.java \ 
                  VertexVector.java \ 
                  VertexTest.java

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.