I am using lat/lon coordinates to map points, then using the "Points to Line" tool to connect the points into a line, such as a road. The lat/lon coordinates are being generated separately for each county in the U.S.. Where the resulting lines are supposed to meet at the county boundary there is a small gap. The attached map illustrates the problem.
I have the same small gap where lines are supposed to join together within a county, which is also illustrated in the attachment.
Bearing in mind I will have thousands of instances where line pairs need to be joined, is there a way to do this with a few commands, or perhaps with a custom tool built in Modelbuilder.
If it helps, the line ends that need to be joined will always be very close together to begin with, they just won't be touching, so any procedure that involves performing an action based on proximity would probably work.
Solved! Go to Solution.
I'm inputting lat/lon coordinates into an excel spreadsheet, drawing them as XY events, then exporting these points to a shapefile. Because I'm limited by the source data, I can only collect lat/lon for one county at a time. So I have to create the lines for County A and County B separately. Hence the reason the lines do not connect at the county line. The fact the lines are in different counties is of no importance to me, and the fact the breaks occur at the county line is immaterial. I just need to connect the lines.
After creating the lines I am projecting them using the Project tool. At no point in this process are any files merged.
A zip file with the three lines is attached
Weird...I couldn't do it with the shapefile that you sent, so I right-clicked on the file, data export data, made a new shapefile, used the extend tool with 1000 ft (way too much) and it worked. Seph's idea requires a value table to specify the relationship, I didn't check it out.
As another suggestion, it may be quicker and easier to edit the locations within the spreadsheet or the output file to ensure that the traversing lines have a common point, but that will depend upon your data structure. Give it a try John
Dan, after your email I was able to replicate what you did. After seeing the result, it dawned on me that the extend line tool merely extends the last line segment to the nearest line, maintaining the angle of the extended line. It does not extend a line from the last vertex to the closest vertex on the nearby line, as I had assumed. Because of this error on my part, the distance I selected for the Extend Line tool reflected the feet from the last vertex to the closest vertex on the nearby line, which was too small, thus producing no result. This is illustrated on the accompanying image. The 1,000 feet you used worked because the maximum distance was about 900 feet.
Xander, the results produced by your code are what I'm looking for, i.e., connecting the last vertex in one line with the first vertex on an adjacent line. However, as you pointed out it doesn't solve the problem of the gap that was on the right side of the illustration I provided. I didn't jump on your code because I don't know nuttin 'bout no Python (or any other programming language for that matter), which is why I was looking for another solution. Thanks to all for the input. I consider this issue closed unless others have commentary.
Also.... give Xander's code a whirl...it does work, just a filename and such to change
specifically this code in his link
- fc_in = r"D:\Xander\GeoNet\Nueva carpeta\test.gdb\lines"
- fc_out = r"D:\Xander\GeoNet\Nueva carpeta\test.gdb\lines_out"
- tolerance = 150 # max distance to snap to a node
Xander Bakker leap in any time you are available
"Leaping" right now ...
So, although the OP prefers not to use python code, I wanted to know if the code works for the shapefile attached to the thread. At the first run an error occurred stating that a NoneType has no firstPoint... mmm, looking at the data it appears that the first geometry is a NoneType. Just calculate the length and see which records show up with a length of 0.
I added two more lines of code (lines 72 and 73) to avoid the errors (see code below). Now it does create a result and we are getting somewhere, but the code only accounts for snapping start and endpoints (nodes) of lines, not an end-point to a vertex. So this is the result:
It snaps the nodes together for the two case on the left (blue circles), but since the line below does not have a node the situation indicated with the red circle is not corrected. The code should be adjusted to account for these situations too.
import math def main(): import arcpy fc_in = r"C:\Forum\SnapLines\Fulton_Dekalb_Proj_GA.shp" fc_out = r"C:\Forum\SnapLines\Fulton_Dekalb_Proj_GA_out_v02.shp" tolerance = 200 # max distance to snap to a node # create dicts dct_lines = getPolylineDict(fc_in) dct_from, dct_to = getFromAndToNodes(dct_lines) # loop through polylines for oid, polyline in dct_lines.items(): # search candidates from point pnt_from = dct_from[oid] dist_min = tolerance + 0.01 pnt_found = None for oid_from, pnt in dct_from.items(): if oid_from != oid: dist = calcDistance(pnt_from, pnt) if dist < dist_min: dist_min = dist pnt_found = arcpy.Point(pnt.X, pnt.Y) for oid_to, pnt in dct_to.items(): if oid_to != oid: dist = calcDistance(pnt_from, pnt) if dist < dist_min: dist_min = dist pnt_found = arcpy.Point(pnt.X, pnt.Y) # change line from point if pnt_found != None: polyline = insertPointAtStart(polyline, pnt_found) dct_lines[oid] = polyline dct_from, dct_to = getFromAndToNodes(dct_lines) # search candidates to points pnt_to = dct_to[oid] dist_min = tolerance + 0.01 pnt_found = None for oid_from, pnt in dct_from.items(): if oid_from != oid: dist = calcDistance(pnt_to, pnt) if dist < dist_min: dist_min = dist pnt_found = arcpy.Point(pnt.X, pnt.Y) for oid_to, pnt in dct_to.items(): if oid_to != oid: dist = calcDistance(pnt_to, pnt) if dist < dist_min: dist_min = dist pnt_found = arcpy.Point(pnt.X, pnt.Y) # change line end point if pnt_found != None: polyline = addPointAtEnd(polyline, pnt_found) dct_lines[oid] = polyline dct_from, dct_to = getFromAndToNodes(dct_lines) # create list of polylines lst_polylines = dct_lines.values() arcpy.CopyFeatures_management(lst_polylines, fc_out) def getPolylineDict(fc_in): dct = {} flds = ("OID@", "SHAPE@") with arcpy.da.SearchCursor(fc_in, flds) as curs: for row in curs: if not row[1] is None: if row[1].length > 0: dct[row[0]] = row[1] del row, curs return dct def getFromAndToNodes(dct): dct_from = {} dct_to = {} for oid, polyline in dct.items(): dct_from[oid] = polyline.firstPoint dct_to[oid] = polyline.lastPoint return dct_from, dct_to def insertPointAtStart(polyline, pnt_found): sr = polyline.spatialReference lst_pnts = [pnt_found] for part in polyline: for pnt in part: lst_pnts.append(pnt) return arcpy.Polyline(arcpy.Array(lst_pnts), sr) def addPointAtEnd(polyline, pnt_found): sr = polyline.spatialReference lst_pnts = [] for part in polyline: for pnt in part: lst_pnts.append(pnt) lst_pnts.append(pnt_found) return arcpy.Polyline(arcpy.Array(lst_pnts), sr) def calcDistance(pnt_to, pnt): return math.hypot(pnt_to.X - pnt.X, pnt_to.Y - pnt.Y) if __name__ == '__main__': main()
Hi John, I believe the snap tool is run on the entire set of features just like extend line. Check out the link I posted.
I did
You did what?
As I read it, the Snap tool doesn't densify the line, but does change the geometry.
that is why you want to densify it or at a minimum add extra vertices towards the end of the line since long lines will change direction and have a significant offset otherwise. This becomes important if there is curvature you are trying to match
One use case for this tool is to rectify the differences in shared or common boundaries between two datasets by snapping the vertices in one boundary to the vertices, edges, or end points of the other. If the input features do not have enough vertices to match the exact curvature of the other boundary, vertices can be added to the input features using the Densify tool to allow for an added level of detail.