2

I have one layer of roads(polylines) and another one that marks the intersections(points) of those roads.

The task is to create laps(one circuit of a racecourse or track) of 2000m, 5000m and so on. They have to be rounds so the startng point is the same as the endpoint.

I have built a network in ArcMap and I did a spatial join between these two layers, but I don't know how to go from there or if I'm even on the right track.

I'd like a solution for ArcMap 10.2.2. The red line is an example of what a lap should look like. Apart from that you can see the points that mark the intersections and the lines.

another example

gerjas
  • 23
  • 4
  • 1
    Please edit your question to better define what a "lap" is, and choose one software solution to conform to the "one question per Question" policy – Vince Aug 07 '16 at 18:16
  • 1
    Please [edit] your question to include a screenshot sample of your data to help visualise what you are asking. – Midavalo Aug 07 '16 at 19:36
  • See if this helps http://gis.stackexchange.com/questions/166230/deleting-polygons-smaller-than-certain-dimension-using-arcgis-for-desktop/166253#166253 – FelixIP Aug 08 '16 at 04:28
  • @FelixIP I don't really see how this could help me. First, I don't need a certain surface area and second, it could be any polygon(not only a rectangle or a circle) the condition being that the perimeter has a certain length and fits the given lines. – gerjas Aug 08 '16 at 08:35
  • Now I see what you are after. How many small polygons are we talking about? Also precisely 2000 might not be possible. Closest option? – FelixIP Aug 08 '16 at 09:44
  • @FelixIP There are 436 small polygons in my layer so I don't want to create all possible polygons by hand and then query for the right perimeter.Yes, I am aware that I might not get exactly 2000m. I could probably work with about hundred meters more or less(so that in this case the perimeter would have to be in the interval from 1900m to 2100m). – gerjas Aug 08 '16 at 11:52
  • This is no ordinary thing to do, i am interested but busy now. Will post possible solution later this week – FelixIP Aug 09 '16 at 05:11

2 Answers2

1

I tested the following seemingly weird idea:

  1. Take a NODE of a dangle-free graph
  2. Find set of nodes that are closer than half of required total length (L), count of them = N
  3. Create lap/path NODE=>permutation of (N,2)=>NODE.
  4. Prohibit using the travelled edges and nodes
  5. Calculate length of lap (l)
  6. Break if abs(L-l)/L < tolerance and select relevant edges/nodes, otherwise set NODE to next node in the graph and go to 1.

Try this idea on below graph and you’ll see that most of all possible lap lengths (3,4,5,6) can be found rather quickly. The only exception is 7.

enter image description here

Strange, but true, it works for much larger networks.

Script below designed to work as a tool to be run from active mxd. It expects to have two layers called “NODES” and “LINKS” in the mxd. The fields that matter are shown below: enter image description here

Field “fi” in links table stores FID of node at the start of the link, “ti” – the same for end of line.

I tested script (you’ll need networkx module installed) on a graph with 750 edges and 509 nodes. Note: result greatly depends on first node(s) location, unless user set findBest equal True. One of the lines shown below was derived using findBest=True option and the same L = 4000 m and it took significantly more time to compute:

enter image description here

Script:

import arcpy, traceback, os, sys
import itertools as itt
sys.path.append(r'C:\Users\felix...\AppData\Roaming\Python\Python27\site-packages')
import networkx as nx

try: def showPyMessage(): arcpy.AddMessage(str(time.ctime()) + " - " + message)

RUN SETTINGS

TARGET=4000
TOLERANCE=0.05

SET TO TRUE TO FIND BEST POSSIBLE SOLUTION

findBest=False

FIND LAYERS and FIELDS

mxd = arcpy.mapping.MapDocument(&quot;CURRENT&quot;)
theNodesLayer=arcpy.mapping.ListLayers(mxd,&quot;NODES&quot;)[0]
result=arcpy.GetCount_management(theNodesLayer)
nNodes=int(result.getOutput(0))
theLinksLayer=arcpy.mapping.ListLayers(mxd,&quot;LINKS&quot;)[0]
arcpy.SelectLayerByAttribute_management(theLinksLayer, &quot;CLEAR_SELECTION&quot;)        
linksFromI,linksToI=&quot;fi&quot;,&quot;ti&quot;

CREATE GRAPH

G=nx.Graph()
with arcpy.da.SearchCursor(theLinksLayer, (&quot;FID&quot;,linksFromI,linksToI,&quot;Length&quot;)) as cursor:
    for m, f,t,c in cursor:
        G.add_edge(f,t,weight=c)
        G[f][t]['rw']=c
        G[f][t]['no']=m

INITIAL CONDITIONS

arcpy.SetProgressor(&quot;default&quot;)
Found=False;ratioMax=100

MAIN LOOP

for ij, node in enumerate(G.nodes()):
    arcpy.AddMessage(&quot;Processing %i out of %i&quot; %(ij,nNodes))
    aBmNodes=[]
    for other in G.nodes():
        L=nx.dijkstra_path_length(G,node,other)
        if L&gt;TARGET/2:continue
        aBmNodes.append(other)
    aBmNodes.remove(node)
    for chain in itt.permutations(aBmNodes, 2):
        arcpy.SetProgressorPosition()
        aList=list(chain)
        one=node; aList.append(one)
        lTotal=0; path=[one]
        for two in aList:
            gL=nx.dijkstra_path(G,one,two)

BLOCKING ATTEMPT to TAKE THE SAME ROAD

            for i,t in enumerate(gL):
                if i==0:
                    f=t; continue
                lTotal+=G[f][t]['weight']
                path.append(t)
                G[f][t]['weight']=1e6
                f=t
            one=two
        f=path.pop(0)

BLOCKING ATTEMPT to GO THROUGH SAME NODE

        control=path[:]
        if len(path)&gt;len(set(control)):
            continue

RESTORE ORIGINAL WEIGHTS

        for t in path:
            G[f][t]['weight']=G[f][t]['rw']
            f=t
        ratioCur=abs(TARGET-lTotal)/TARGET
        if ratioCur&lt;ratioMax:
            bestList=aList[:]
            bestPath=path[:]
            ratioMax=ratioCur
            arcpy.AddMessage('Best match so far %i' %lTotal)
        if abs(TARGET-lTotal)/TARGET&lt;=TOLERANCE:
            Found=True
            if not findBest:break
    if Found and not findBest:break

SELECT NODES TRIO and LINKS for LAP

quer='&quot;FID&quot; IN '+str(tuple(bestList))
arcpy.SelectLayerByAttribute_management(theNodesLayer, &quot;NEW_SELECTION&quot;, quer)        
f=bestPath[-1]; ListOfLinks=[]
for t in bestPath:
    ListOfLinks.append(G[f][t]['no'])
    f=t
quer='&quot;FID&quot; IN '+str(tuple(ListOfLinks))
arcpy.SelectLayerByAttribute_management(theLinksLayer, &quot;NEW_SELECTION&quot;, quer)        

except: message = "\n*** PYTHON ERRORS *** "; showPyMessage() message = "Python Traceback Info: " + traceback.format_tb(sys.exc_info()[2])[0]; showPyMessage() message = "Python Error Info: " + str(sys.exc_type)+ ": " + str(sys.exc_value) + "\n"; showPyMessage()

FelixIP
  • 22,922
  • 3
  • 29
  • 61
  • Thanks. Sorry for the late answer, I was a little busy and it took me some time to apply your solution to my data. – gerjas Sep 11 '16 at 21:40
0

If you have an advanced license you could use the Feature to Polygon tool and then query for polygon features that have a perimeter close to what you require.

Kirk Kuykendall
  • 25,787
  • 8
  • 65
  • 153
  • This comes close but I'd also need the polygons merged with their neighbouring ones in all variations(with one, two or three neighbours and then with THEIR neighbours...) so that I would have far more than 8 polygons in the second screenshot. – gerjas Aug 07 '16 at 21:45