I have buildings and road shapefile. I am creating centroids of building and connecting it to nearest road feature as shown in fig 1.

Code to draw a line between building centroid and road:

import arcpy
road = r"...\road.shp"
buildings = r"...\building.shp"

arcpy.env.workspace =  arcpy.Describe(buildings).path
centroid = os.path.join(arcpy.env.workspace, "centroid.shp") 
connectingLines = os.path.join(arcpy.env.workspace, "connectingLines.shp")

arcpy.FeatureToPoint_management(buildings, centroid, "CENTROID")

arcpy.AddXY_management(centroid) #Adding POINT_X and POINT_Y
arcpy.Near_analysis(centroid, rd, "", "LOCATION", "NO_ANGLE" ) #Adding NEAR_X and NEAR_Y
arcpy.XYToLine_management(centroid, connectingLines, "POINT_X", "POINT_Y", "NEAR_X", "NEAR_Y")

enter image description here

However, what i want is shown in fig 2. If the line connecting road and centroid of building passes through another building then the connecting line should connect to the nearest centroid of a building and not the road.

enter image description here

Possible solution but not complete: Draw the line between centroid and road as shown in fig 1. Delete all lines passing through more than one building. Divide the centroid points as connected and disconnected. Join the disconnected points to nearest connected points. Again delete the lines passing through more than two buildings and repeat process until all points are connected.

Limitation: does not connect centroid of buildings that need to cross more than 2 buildings before connecting to closest connected point.

 import arcpy,os, math
 from os.path import basename, dirname, exists, join

 rd = r"...\road.shp"
 buildings = r"...\building.shp"

 arcpy.env.workspace =  arcpy.Describe(buildings).path

 # remove lines that intersect with more than one/two buildings 
 def RemoveLine(fc, cnt):
    arcpy.AddField_management(fc, "count", "INTEGER" )

    with arcpy.da.UpdateCursor(fc, ['count', 'SHAPE@']) as cursor:
         for row in cursor:        
            arcpy.MakeFeatureLayer_management(buildings, "layer")
            arcpy.SelectLayerByLocation_management("layer", 'INTERSECT', row[1])
            result = arcpy.GetCount_management("layer")
            row[0] = int(result.getOutput(0))             

    if cnt == 1:    
       arcpy.MakeFeatureLayer_management(fc, "tempLayer")
       arcpy.SelectLayerByAttribute_management("tempLayer",  "NEW_SELECTION", '"count" > 1')

       arcpy.MakeFeatureLayer_management(fc, "tempLayer")
       arcpy.SelectLayerByAttribute_management("tempLayer",  "NEW_SELECTION", '"count" > 2')

 centroid = os.path.join(arcpy.env.workspace, "b_centroid.shp")

 arcpy.FeatureToPoint_management(buildings, centroid, "CENTROID")

 centroidToRd = os.path.join(arcpy.env.workspace, "centroidToRd.shp")
 arcpy.AddXY_management(centroid) # Adding POINT_X and POINT_Y
 arcpy.Near_analysis(centroid, rd, "", "LOCATION", "NO_ANGLE" ) # Adding NEAR_X and NEAR_Y
 arcpy.XYToLine_management(centroid, centroidToRd, "POINT_X", "POINT_Y", "NEAR_X", "NEAR_Y")

 RemoveLine(centroidToRd, 1)

 connectingLines = os.path.join(arcpy.env.workspace, "ConnectingLines.shp")
 arcpy.CopyFeatures_management(centroidToRd, connectingLines)

 cnt = 1
 itr = 0
 arr = [centroidToRd]

 #iterate through centroid points until each one is connected to closest centroid or directly to road

 while (cnt > 0  and itr < 5):

     itr +=1

     unique_name = arcpy.CreateUniqueName("feeder.shp")
     arcpy.MakeFeatureLayer_management(centroid, "connected")
     arcpy.SelectLayerByLocation_management("connected", 'INTERSECT',    connectingLines, invert_spatial_relationship = "NOT_INVERT")

     arcpy.MakeFeatureLayer_management(centroid, "disconnected")
     arcpy.SelectLayerByLocation_management("disconnected", 'INTERSECT', connectingLines, invert_spatial_relationship = "INVERT")
     cnt = arcpy.GetCount_management("disconnected")
     #print cnt

     arcpy.AddXY_management("disconnected") # Adding POINT_X and POINT_Y
     arcpy.Near_analysis("disconnected", "connected", "", "LOCATION", "NO_ANGLE" ) # Adding NEAR_X and NEAR_Y
     arcpy.XYToLine_management("disconnected", unique_name, "POINT_X", "POINT_Y", "NEAR_X", "NEAR_Y")

     RemoveLine(unique_name, 2)

    #print arr

     connectingLines = os.path.join(arcpy.env.workspace, "ConnectingLines.shp")
     arcpy.Merge_management(arr, connectingLines)
  • You'll need to use an UpdateCursor (DA, of course) to wak each feature to determine if a centroid is closer (and in the right direction).
    – Vince
    Commented Mar 22, 2017 at 13:47
  • 1
    The shortest line is the line to keep as all others feed into it in fig2. Also nearest centroid will mean the two smallest buildings will attach to each other, in that case you want to attach to a further centroid which happens to be nearer to road.
    – Hornbydd
    Commented Mar 22, 2017 at 13:56

1 Answer 1


You could try this tool: SpanningTreeTools:

Tools for creating several variants of the euclidean minimum spanning tree as well as desire lines.

I tried the EMST tool with ArcMap 10.5 on a point layer of building centroids and nearest road. Maybe it is not exactly what you are looking for but worth a try. It it written in Python.

enter image description here

I used it in a model with Iterate Feature Selection.

  • Interesting tool. The model is rather slow for large number of points but the tool itself could be useful for various purpose. I will test to integrate it with rest of my program. Thank you.
    – Jio
    Commented Mar 29, 2017 at 14:08

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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