def __init__(self, network): """ Test tag <tc>#is#Snapper.__init__</tc> """ """ Generate a list of the edge lengths and pick a maximum length allowed based on the median length. This maximum length will be used to use multiple midpoints to represent edges which are exceptionally long, lest they ruin the efficiency of the algorithm. """ # Generate list of lengths self.network = network edge_lens = [] for n in network: for m in network[n]: if n != m: edge_lens.append(pysal.cg.get_points_dist(Point(n), Point(m))) # it can be optional if edge_lens == []: raise ValueError, "Network has no positive-length edges" edge_lens.sort() max_allowed_edge_len = 5 * edge_lens[len(edge_lens) / 2] """ Create a bin structures with proper range to hold all of the edges. The size of the bin is on the order of the length of the longest edge (and of the neighborhoods searched around each query point. """ endpoints = network.keys() endpoints_start, endpoints_end = [ep[0] for ep in endpoints], [ep[1] for ep in endpoints] bounds = Rectangle(min(endpoints_start), min(endpoints_end), max(endpoints_start), max(endpoints_end)) self.grid = Grid(bounds, max_allowed_edge_len) """ Insert the midpoint of each edge into the grid. If an edge is too long, break it into edges of length less than the maximum allowed length and add the midpoint of each. """ self.search_rad = max_allowed_edge_len * 0.55 for n in network: for m in network[n]: edge_len = pysal.cg.get_points_dist(Point(n), Point(m)) # it can be a direct extraction if edge_len > max_allowed_edge_len: mid_edge = [] num_parts = int(math.ceil(edge_len / max_allowed_edge_len)) part_step = 1.0 / num_parts dx = m[0] - n[0] dy = m[1] - n[1] midpoint = (n[0] + dx * part_step / 2, n[1] + dy * part_step / 2) for r in [part_step * t for t in xrange(num_parts)]: mid_edge.append(((n, m), midpoint)) midpoint = (midpoint[0] + dx * part_step, midpoint[1] + dy * part_step) for me in mid_edge: self.grid.add(me[0], Point(me[1])) else: self.grid.add((n, m), Point(((n[0] + m[0]) / 2, (n[1] + m[1]) / 2))) """ During the snapping of a query point we will initialize the closest point on the network to be a dummy location known to be invalid. This must be done in case the neighborhood search does not find any edge midpoints and it must be grown repeatedly. In this case we want to make sure we don't give up having not found a valid closest edge. """ self.dummy_proj = (None, None, 0, 0) # Src, dest, dist_from_src, dist_from_dest)
def __init__(self, network): """ Test tag <tc>#is#Snapper.__init__</tc> """ """ Generate a list of the edge lengths and pick a maximum length allowed based on the median length. This maximum length will be used to use multiple midpoints to represent edges which are exceptionally long, lest they ruin the efficiency of the algorithm. """ # Generate list of lengths self.network = network edge_lens = [] for n in network: for m in network[n]: if n != m: edge_lens.append( pysal.cg.get_points_dist( Point(n), Point(m))) # it can be optional if edge_lens == []: raise ValueError('Network has no positive-length edges') edge_lens.sort() max_allowed_edge_len = 5 * edge_lens[len(edge_lens) / 2] """ Create a bin structures with proper range to hold all of the edges. The size of the bin is on the order of the length of the longest edge (and of the neighborhoods searched around each query point. """ endpoints = list(network.keys()) endpoints_start, endpoints_end = [ep[0] for ep in endpoints ], [ep[1] for ep in endpoints] bounds = Rectangle(min(endpoints_start), min(endpoints_end), max(endpoints_start), max(endpoints_end)) self.grid = Grid(bounds, max_allowed_edge_len) """ Insert the midpoint of each edge into the grid. If an edge is too long, break it into edges of length less than the maximum allowed length and add the midpoint of each. """ self.search_rad = max_allowed_edge_len * 0.55 for n in network: for m in network[n]: edge_len = pysal.cg.get_points_dist( Point(n), Point(m)) # it can be a direct extraction if edge_len > max_allowed_edge_len: mid_edge = [] num_parts = int(math.ceil(edge_len / max_allowed_edge_len)) part_step = 1.0 / num_parts dx = m[0] - n[0] dy = m[1] - n[1] midpoint = (n[0] + dx * part_step / 2, n[1] + dy * part_step / 2) for r in [part_step * t for t in range(num_parts)]: mid_edge.append(((n, m), midpoint)) midpoint = (midpoint[0] + dx * part_step, midpoint[1] + dy * part_step) for me in mid_edge: self.grid.add(me[0], Point(me[1])) else: self.grid.add((n, m), Point( ((n[0] + m[0]) / 2, (n[1] + m[1]) / 2))) """ During the snapping of a query point we will initialize the closest point on the network to be a dummy location known to be invalid. This must be done in case the neighborhood search does not find any edge midpoints and it must be grown repeatedly. In this case we want to make sure we don't give up having not found a valid closest edge. """ self.dummy_proj = (None, None, 0, 0 ) # Src, dest, dist_from_src, dist_from_dest)
class Snapper: """ Snaps points to their nearest location on the network. Uses a novel algorithm which relies on two properties of the input network: 1. Most of the edges are very short relative to the total area encompassed by the network. 2. The edges have a relatively constant density throughout this area. The algorithm works by creating a binning of the midpoints of all the edges. When a query point is given, all the edges in a region around the query point (located by their binned midpoints) are compared to the query point. If none are found, the neighborhood is enlarged. When a closest edge is found, the neighborhood is enlarged slightly and checked again. The enlargement is such that if the closest edge found remains the closest edge, then it will always be the closest edge. """ def __init__(self, network): """ Test tag <tc>#is#Snapper.__init__</tc> """ """ Generate a list of the edge lengths and pick a maximum length allowed based on the median length. This maximum length will be used to use multiple midpoints to represent edges which are exceptionally long, lest they ruin the efficiency of the algorithm. """ # Generate list of lengths self.network = network edge_lens = [] for n in network: for m in network[n]: if n != m: edge_lens.append(pysal.cg.get_points_dist(Point(n), Point(m))) # it can be optional if edge_lens == []: raise ValueError, "Network has no positive-length edges" edge_lens.sort() max_allowed_edge_len = 5 * edge_lens[len(edge_lens) / 2] """ Create a bin structures with proper range to hold all of the edges. The size of the bin is on the order of the length of the longest edge (and of the neighborhoods searched around each query point. """ endpoints = network.keys() endpoints_start, endpoints_end = [ep[0] for ep in endpoints], [ep[1] for ep in endpoints] bounds = Rectangle(min(endpoints_start), min(endpoints_end), max(endpoints_start), max(endpoints_end)) self.grid = Grid(bounds, max_allowed_edge_len) """ Insert the midpoint of each edge into the grid. If an edge is too long, break it into edges of length less than the maximum allowed length and add the midpoint of each. """ self.search_rad = max_allowed_edge_len * 0.55 for n in network: for m in network[n]: edge_len = pysal.cg.get_points_dist(Point(n), Point(m)) # it can be a direct extraction if edge_len > max_allowed_edge_len: mid_edge = [] num_parts = int(math.ceil(edge_len / max_allowed_edge_len)) part_step = 1.0 / num_parts dx = m[0] - n[0] dy = m[1] - n[1] midpoint = (n[0] + dx * part_step / 2, n[1] + dy * part_step / 2) for r in [part_step * t for t in xrange(num_parts)]: mid_edge.append(((n, m), midpoint)) midpoint = (midpoint[0] + dx * part_step, midpoint[1] + dy * part_step) for me in mid_edge: self.grid.add(me[0], Point(me[1])) else: self.grid.add((n, m), Point(((n[0] + m[0]) / 2, (n[1] + m[1]) / 2))) """ During the snapping of a query point we will initialize the closest point on the network to be a dummy location known to be invalid. This must be done in case the neighborhood search does not find any edge midpoints and it must be grown repeatedly. In this case we want to make sure we don't give up having not found a valid closest edge. """ self.dummy_proj = (None, None, 0, 0) # Src, dest, dist_from_src, dist_from_dest) def snap(self, p): """ Test tag <tc>#is#Snapper.snap</tc> """ """ Initialize the closest location found so far to be infinitely far away. Then begin with a neighborhood on the order of the maximum edge allowed and repeatedly growing it. When a closest edge is found, grow once more and check again. """ cur_s_rad = self.search_rad found_something = False # Whle neighborhood is empty, enlarge and check again while not found_something: if self.grid.proximity(Point(p), cur_s_rad) != []: found_something = True cur_s_rad *= 2 # Expand to include any edges whose endpoints might lie just outside # the search radius cur_s_rad += self.search_rad # Now find closest in this neighborhood best_seg_dist = 1e600 for e in self.grid.proximity(Point(p), cur_s_rad): seg = LineSegment(Point(e[0]), Point(e[1])) p2seg = pysal.cg.get_segment_point_dist(seg, Point(p)) dist = p2seg[0] if p2seg[0] < best_seg_dist: # (src, dest, dist_from_src, dist_from_dest) best_proj = (e[0], e[1], dist * p2seg[1], dist * (1 - p2seg[1])) best_seg_dist = p2seg[0] return best_proj
class Snapper: """ Snaps points to their nearest location on the network. Uses a novel algorithm which relies on two properties of the input network: 1. Most of the edges are very short relative to the total area encompassed by the network. 2. The edges have a relatively constant density throughout this area. The algorithm works by creating a binning of the midpoints of all the edges. When a query point is given, all the edges in a region around the query point (located by their binned midpoints) are compared to the query point. If none are found, the neighborhood is enlarged. When a closest edge is found, the neighborhood is enlarged slightly and checked again. The enlargement is such that if the closest edge found remains the closest edge, then it will always be the closest edge. """ def __init__(self, network): """ Test tag <tc>#is#Snapper.__init__</tc> """ """ Generate a list of the edge lengths and pick a maximum length allowed based on the median length. This maximum length will be used to use multiple midpoints to represent edges which are exceptionally long, lest they ruin the efficiency of the algorithm. """ # Generate list of lengths self.network = network edge_lens = [] for n in network: for m in network[n]: if n != m: edge_lens.append( pysal.cg.get_points_dist( Point(n), Point(m))) # it can be optional if edge_lens == []: raise ValueError('Network has no positive-length edges') edge_lens.sort() max_allowed_edge_len = 5 * edge_lens[len(edge_lens) / 2] """ Create a bin structures with proper range to hold all of the edges. The size of the bin is on the order of the length of the longest edge (and of the neighborhoods searched around each query point. """ endpoints = list(network.keys()) endpoints_start, endpoints_end = [ep[0] for ep in endpoints ], [ep[1] for ep in endpoints] bounds = Rectangle(min(endpoints_start), min(endpoints_end), max(endpoints_start), max(endpoints_end)) self.grid = Grid(bounds, max_allowed_edge_len) """ Insert the midpoint of each edge into the grid. If an edge is too long, break it into edges of length less than the maximum allowed length and add the midpoint of each. """ self.search_rad = max_allowed_edge_len * 0.55 for n in network: for m in network[n]: edge_len = pysal.cg.get_points_dist( Point(n), Point(m)) # it can be a direct extraction if edge_len > max_allowed_edge_len: mid_edge = [] num_parts = int(math.ceil(edge_len / max_allowed_edge_len)) part_step = 1.0 / num_parts dx = m[0] - n[0] dy = m[1] - n[1] midpoint = (n[0] + dx * part_step / 2, n[1] + dy * part_step / 2) for r in [part_step * t for t in range(num_parts)]: mid_edge.append(((n, m), midpoint)) midpoint = (midpoint[0] + dx * part_step, midpoint[1] + dy * part_step) for me in mid_edge: self.grid.add(me[0], Point(me[1])) else: self.grid.add((n, m), Point( ((n[0] + m[0]) / 2, (n[1] + m[1]) / 2))) """ During the snapping of a query point we will initialize the closest point on the network to be a dummy location known to be invalid. This must be done in case the neighborhood search does not find any edge midpoints and it must be grown repeatedly. In this case we want to make sure we don't give up having not found a valid closest edge. """ self.dummy_proj = (None, None, 0, 0 ) # Src, dest, dist_from_src, dist_from_dest) def snap(self, p): """ Test tag <tc>#is#Snapper.snap</tc> """ """ Initialize the closest location found so far to be infinitely far away. Then begin with a neighborhood on the order of the maximum edge allowed and repeatedly growing it. When a closest edge is found, grow once more and check again. """ cur_s_rad = self.search_rad found_something = False # Whle neighborhood is empty, enlarge and check again while not found_something: if self.grid.proximity(Point(p), cur_s_rad) != []: found_something = True cur_s_rad *= 2 # Expand to include any edges whose endpoints might lie just outside # the search radius cur_s_rad += self.search_rad # Now find closest in this neighborhood best_seg_dist = 1e600 for e in self.grid.proximity(Point(p), cur_s_rad): seg = LineSegment(Point(e[0]), Point(e[1])) p2seg = pysal.cg.get_segment_point_dist(seg, Point(p)) dist = p2seg[0] if p2seg[0] < best_seg_dist: # (src, dest, dist_from_src, dist_from_dest) best_proj = (e[0], e[1], dist * p2seg[1], dist * (1 - p2seg[1])) best_seg_dist = p2seg[0] return best_proj