def minpath( p, q, roadmap, length_attr='length' ) :
    """ returns one of the min length paths between two points on a Roadmap """
    # some convenient static variables
    if not hasattr( minpath, 'STATICS' ) :
        minpath.STATICS = True
        minpath.first = RoadAddress(None,None)
        minpath.second = RoadAddress(None,None)
        minpath.points = [ minpath.first, minpath.second ]
    
    # convenient non-static variables
    road        = p.road
    edge, data  = obtain_edge( roadmap, road, True )
    i,j,key     = edge
    roadlen     = data.get( length_attr, 1 )
    
    # we will be choosing best path among options
    options = []
    
    # one possible type of path is a segment on a single road
    path = minpath_on_road( p, q, road, roadmap, length_attr )
    options.append( ( pathLength(path), path ) )
    
    # two more possible types of paths are p->u->q, for u either of endpoints of road of p
    minpath.first.init(road,0.)
    minpath.second.init(road,roadlen)
    for u, qq in zip( (i,j), minpath.points ) :
        first = minpath_on_road( p, qq, road, roadmap, length_attr )
        second = minpath_node_to_point( u, q, roadmap, length_attr )
        path = first + second
        options.append( ( pathLength(path), path ) )
        
    # return path having the shortest length --- might be []
    return min( options )[1]
 def address_coord(cls, addr, roadnet, layout, length='length' ) :
     if not isinstance( addr, ROAD.RoadAddress ) : raise 'not a valid RoadAddress'
     
     edge, road_data = ROAD.obtain_edge( roadnet, addr.road, True )
     u,v,key = edge
     
     pt1 = layout[u] ; pt2 = layout[v]
     dEuc = np.linalg.norm( pt2 - pt1 )
     d = road_data.get( length, 1 )
     
     if dEuc < d :
         """ on arc """
         center, R, t1, t2 = fitArc2Segment( pt1, pt2, d )
         theta = angle_interpolate_ccw( t1, t2, addr.coord )
         coord = center + R * np.array([ np.cos(theta), np.sin(theta) ])
     else :
         """ segment part """
         idx = lnet.edge[i][j].keys().index(key)
         if idx == 0 :
             """ on "shorty" """
             mid = ( pt1 + pt2 ) / 2
             offset = ( d/dEuc / 2 ) * ( pt2 - pt1 )
             pt1_inner, pt2_inner = mid-offset, mid+offset
             coord = x * pt2_inner + ( 1.0 - x ) * pt1_inner
         else :
             """ on one of the spaced arcs """
             spacing = (-1)**idx * np.ceil( float(idx) / 2 )
             ht = ( dEuc / 20 ) * spacing
             center, R, theta1, theta2 = fitArcThruSpacing( pt1, pt2, ht )
             if ht > 0. :
                 dtheta = angleconvert_zerototwopi( theta2 - theta1 )
                 darc = R * dtheta
                 y = d / darc
                 t1 = angle_interpolate_ccw( theta1, theta2, 0.5 - y/2 )
                 t2 = angle_interpolate_ccw( theta1, theta2, 0.5 + y/2 )
                 theta = angle_interpolate_ccw( t1, t2, x )
             else :
                 dtheta = angleconvert_zerototwopi( theta1 - theta2 )
                 darc = R * dtheta
                 y = d / darc
                 t1 = angle_interpolate_ccw( theta1, theta2, 0.5 - y/2, reverse=True )
                 t2 = angle_interpolate_ccw( theta1, theta2, 0.5 + y/2, reverse=True )
                 theta = angle_interpolate_ccw( t1, t2, x, reverse=True )
             coord = center + R * np.array([ np.cos(theta), np.sin(theta) ])
     return coord
def pathFromAStar( astar_path, roadmap, length_attr ) :
    path = []
    
    nodewalk = astar_path[::2]
    edgewalk = astar_path[1::2]
    for i,j, edgedata in zip( nodewalk[:-1], nodewalk[1:], edgewalk ) :
        _,__,road = edgedata
        edge, data  = obtain_edge( roadmap, road, True )
        u,v,_       = edge
        roadlen     = data.get( length_attr, 1 )
        
        if i == u and j == v :  # forward jump, yay!
            segment = RoadSegment( road, 0., roadlen )
        elif i == v and j == u : # backward jump, yay!
            segment = RoadSegment( road, roadlen, 0. )
        else :
            raise Exception('invalid hop on roadmap')
        
        path.append( segment )
    
    return path
def minpath_on_road( p, q, road, roadmap, length_attr='length' ) :
    """
    subroutine and useful utility:
    returns the path from p to q on road, if the direction of travel is admissible;
    if not, or if p and q are not co-'road'-al, returns empty path, implying non-existence 
    """ 
    edge, data  = obtain_edge( roadmap, road, True )
    i,j,key     = edge
    
    path = []
    # regular logic, NOT error catching
    try :
        assert p.road == road and check_point( roadmap, p, length_attr )
        assert q.road == road and check_point( roadmap, q, length_attr )
        assert p.coord < q.coord or not data.get( 'oneway', False )
        path.append( RoadSegment( road, p.coord, q.coord ) )
        
    except AssertionError :
        pass
    
    return path
def minpath_node_to_point( u, q, roadmap, length_attr='length' ) :
    """
    subroutine and useful utility:
    returns the shortest-path distance from a node u to a point q, on digraph    
    """
    
    # some convenient static variables
    scope = minpath_node_to_point
    if not hasattr( scope, 'STATICS' ) :
        scope.STATICS = True
        scope.first = RoadAddress(None,None)
        scope.second = RoadAddress(None,None)
        scope.points = [ scope.first, scope.second ]
    
    # convenient non-static variables
    assert check_point( roadmap, q, length_attr )
    road        = q.road
    edge, data  = obtain_edge( roadmap, road, True )
    i,j,key     = edge
    roadlen = data.get( length_attr, 1 )
    
    # pre-load options with nopath; in case there are no feasible paths
    options = [ (np.inf, [] ) ]
    
    # possible paths are u->v->q, for v in endpoints of road of q
    scope.first.init(road,0.)
    scope.second.init(road,roadlen)
    for v, pp in zip( (i,j), scope.points ) :
        try :
            astarPath = astar_basic.astar_path( roadmap, u, v, None, weight=length_attr )
            first = pathFromAStar( astarPath, roadmap, length_attr )
        except nx.NetworkXNoPath :
            continue
        second = minpath_on_road( pp, q, road, roadmap, length_attr )
        path = first + second
        options.append( (pathLength(path), path ) )
        
    return min( options )[1]
def roadinfo( road, roadnet, length='length' ) :
    edge, data  = ROAD.obtain_edge( roadnet, road, True )
    u, v, key   = edge
    roadlen     = data.get( length, 1 )
    #
    return u, v, roadlen
def sample_onroad( road, roadnet, length='length' ) :
    """ samples uniformly from the given road """
    _, road_data = ROAD.obtain_edge( roadnet, road, True )
    roadlen = road_data.get( length, 1 )
    y = roadlen * np.random.rand()
    return ROAD.RoadAddress(road,y)