示例#1
0
def compute_adjacency_list(input_points, input_network, id_attribute,
                           impedance_attribute, accumulator_attributes,
                           search_radius, output_location, adj_dbf_name):
    """
  |input_points|: point shape file marking entity (e.g. building) locations
  |input_network|: street network in which |input_points| is located
  |id_attribute|: the name of attribute that distinguishes between input points
  |impedance_attribute|: distance between neighboring nodes will be based on
      this attribute
  |accumulator_attributes|: distance between neighboring nodes will also be
      recorded for these attributes
  |search_radius|: the maximum extent for centrality computation
  |output_location|: adjacency list dbf will be saved here
  |adj_dbf_name|: the name of the adjacency list dbf
  """

    # Number of points in |input_points|
    input_point_count = int(GetCount_management(input_points).getOutput(0))

    # Make a directory to store all auxiliary files
    auxiliary_dir = join(output_location, AUXILIARY_DIR_NAME)
    if not Exists(auxiliary_dir):
        mkdir(auxiliary_dir)

    # Record the edge and junction source names of |input_network|
    junction_feature, edge_feature = network_features(input_network)

    # Calculate network locations if not already calculated
    test_input_point = UpdateCursor(input_points).next()
    locations_calculated = all(
        row_has_field(test_input_point, field)
        for field in NETWORK_LOCATION_FIELDS)
    if not locations_calculated:
        calculate_network_locations(input_points, input_network)

    # Calculate barrier cost per input point if not already calculated
    barrier_costs_calculated = row_has_field(test_input_point,
                                             trim(BARRIER_COST_FIELD))
    if not barrier_costs_calculated:
        AddMessage(BARRIER_COST_COMPUTATION_STARTED)
        # Add |BARRIER_COST_FIELD| column in |input_points|
        AddField_management(in_table=input_points,
                            field_name=trim(BARRIER_COST_FIELD),
                            field_type="DOUBLE",
                            field_is_nullable="NON_NULLABLE")

        # Initialize a dictionary to store the frequencies of (SnapX, SnapY) values
        xy_count = {}
        # A method to retrieve a (SnapX, SnapY) pair for a row in |input_points|
        get_xy = lambda row: (row.getValue(trim("SnapX")),
                              row.getValue(trim("SnapY")))

        barrier_pre_progress = Progress_Bar(input_point_count, 1,
                                            BARRIER_COST_PRE_PROCESSING)
        rows = UpdateCursor(input_points)
        for row in rows:
            snap_xy = get_xy(row)
            if snap_xy in xy_count:
                xy_count[snap_xy] += 1
            else:
                xy_count[snap_xy] = 1
            barrier_pre_progress.step()

        # Populate |BARRIER_COST_FIELD|, this will be used in OD matrix computation
        barrier_progress = Progress_Bar(input_point_count, 1,
                                        BARRIER_COST_COMPUTATION)
        rows = UpdateCursor(input_points)
        for row in rows:
            barrier_cost = BARRIER_COST / xy_count[get_xy(row)]
            row.setValue(trim(BARRIER_COST_FIELD), barrier_cost)
            rows.updateRow(row)
            barrier_progress.step()
        AddMessage(BARRIER_COST_COMPUTATION_FINISHED)

    # Necessary files
    od_cost_matrix_layer = join(auxiliary_dir, OD_COST_MATRIX_LAYER_NAME)
    od_cost_matrix_lines = join(od_cost_matrix_layer, OD_COST_MATRIX_LINES)
    temp_adj_dbf_name = TEMP_ADJACENCY_DBF_NAME(adj_dbf_name)
    temp_adj_dbf = join(output_location, temp_adj_dbf_name)
    adj_dbf = join(output_location, adj_dbf_name)
    partial_adj_dbf = join(auxiliary_dir, PARTIAL_ADJACENCY_LIST_NAME)
    polygons = join(auxiliary_dir, POLYGONS_SHAPEFILE_NAME)
    raster = join(auxiliary_dir, RASTER_NAME)
    polygons_layer = join(auxiliary_dir, POLYGONS_LAYER_NAME)
    input_points_layer = join(auxiliary_dir, INPUT_POINTS_LAYER_NAME)

    # Make sure none of these files already exists
    for path in [
            od_cost_matrix_layer, temp_adj_dbf, adj_dbf, partial_adj_dbf,
            polygons, raster, polygons_layer, input_points_layer,
            od_cost_matrix_lines
    ]:
        delete(path)

    # Cutoff radius for OD matrix computation
    cutoff_radius = 2 * BARRIER_COST + min(search_radius, BARRIER_COST / 2)

    # Compute OD matrix
    MakeODCostMatrixLayer_na(in_network_dataset=input_network,
                             out_network_analysis_layer=od_cost_matrix_layer,
                             impedance_attribute=impedance_attribute,
                             default_cutoff=str(cutoff_radius),
                             accumulate_attribute_name=accumulator_attributes,
                             UTurn_policy="ALLOW_UTURNS",
                             hierarchy="NO_HIERARCHY",
                             output_path_shape="NO_LINES")

    # Determine raster cell size
    points_per_raster_cell = OD_MATRIX_ENTRIES / input_point_count
    raster_cell_count = max(1, input_point_count / points_per_raster_cell)
    input_points_extent = Describe(input_points).Extent
    raster_cell_area = (input_points_extent.width *
                        input_points_extent.height / raster_cell_count)
    raster_cell_size = int(sqrt(raster_cell_area))

    # Construct |raster| from |input_points|
    PointToRaster_conversion(in_features=input_points,
                             value_field=id_attribute,
                             out_rasterdataset=raster,
                             cell_assignment="MOST_FREQUENT",
                             priority_field="NONE",
                             cellsize=str(raster_cell_size))

    # Construct |polygons| from |raster|
    RasterToPolygon_conversion(in_raster=raster,
                               out_polygon_features=polygons,
                               simplify="NO_SIMPLIFY",
                               raster_field="VALUE")

    # Export empty |od_cost_matrix_lines| to |temp_dbf| to start adjacency list
    TableToTable_conversion(in_rows=od_cost_matrix_lines,
                            out_path=output_location,
                            out_name=temp_adj_dbf_name)

    # Construct |polygons_layer| and |input_points_layer|
    for (feature, layer) in [(polygons, polygons_layer),
                             (input_points, input_points_layer)]:
        MakeFeatureLayer_management(in_features=feature, out_layer=layer)

    def add_locations(sub_layer, field_mappings=""):
        """
    |sub_layer|: one of "Origins", "Destinations", "Barrier Points"
    |field_mappings|: field mappings in addition to those for "Name" and
        "CurbApproach"
    """
        AddLocations_na(in_network_analysis_layer=od_cost_matrix_layer,
                        sub_layer=sub_layer,
                        in_table=input_points_layer,
                        field_mappings=("Name %s #; CurbApproach # 0; %s" %
                                        (id_attribute, field_mappings)),
                        search_tolerance=SEARCH_TOLERANCE,
                        search_criteria=("%s SHAPE; %s SHAPE;" %
                                         (junction_feature, edge_feature)),
                        append="CLEAR",
                        snap_to_position_along_network="SNAP",
                        snap_offset=SNAP_OFFSET)

    # OD cost matrix destinations
    AddMessage(ADDING_DESTINATIONS_STARTED)
    SelectLayerByLocation_management(in_layer=input_points_layer)
    add_locations("Destinations")
    AddMessage(ADDING_DESTINATIONS_FINISHED)

    # OD cost matrix point barriers
    AddMessage(ADDING_BARRIERS_STARTED)
    add_locations("Point Barriers",
                  ("FullEdge # 0; BarrierType # 2;"
                   "Attr_%s %s #;" %
                   (impedance_attribute, trim(BARRIER_COST_FIELD))))
    AddMessage(ADDING_BARRIERS_FINISHED)

    # Compute adjacency list, one raster cell at a time
    progress = Progress_Bar(raster_cell_count, 1, STEP_1)
    rows = UpdateCursor(polygons)
    for row in rows:
        # Select the current polygon
        SelectLayerByAttribute_management(in_layer_or_view=polygons_layer,
                                          selection_type="NEW_SELECTION",
                                          where_clause="FID = %s" %
                                          str(row.FID))

        # Origins
        SelectLayerByLocation_management(in_layer=input_points_layer,
                                         select_features=polygons_layer)
        add_locations("Origins")

        # Solve OD Cost matrix
        Solve_na(in_network_analysis_layer=od_cost_matrix_layer,
                 ignore_invalids="SKIP")

        # Add origin and destination fields to the adjacency list dbf
        for (index, field) in [(0, ORIGIN_ID_FIELD_NAME),
                               (1, DESTINATION_ID_FIELD_NAME)]:
            CalculateField_management(in_table=od_cost_matrix_lines,
                                      field=field,
                                      expression="!Name!.split(' - ')[%d]" %
                                      index,
                                      expression_type="PYTHON")

        # Record actual distance between neighboring nodes
        distance_field = "Total_%s" % impedance_attribute
        CalculateField_management(in_table=od_cost_matrix_lines,
                                  field=distance_field,
                                  expression="!%s! - 2 * %d" %
                                  (distance_field, BARRIER_COST),
                                  expression_type="PYTHON")

        # Append result to |temp_adj_dbf|
        TableToTable_conversion(in_rows=od_cost_matrix_lines,
                                out_path=auxiliary_dir,
                                out_name=PARTIAL_ADJACENCY_LIST_NAME)
        Append_management(inputs=partial_adj_dbf,
                          target=temp_adj_dbf,
                          schema_type="TEST")

        progress.step()

    # Copy data from |temp_adj_dbf| to |adj_dbf|
    Rename_management(in_data=temp_adj_dbf, out_data=adj_dbf)

    # Clean up
    for path in [
            od_cost_matrix_layer, partial_adj_dbf, polygons, raster,
            polygons_layer, input_points_layer, auxiliary_dir
    ]:
        delete(path)
示例#2
0
def compute_centrality(nodes, origins, compute_r, compute_g, compute_b,
    compute_c, compute_s, radius, network_radius, beta, measures_to_normalize,
    accumulator_fields):
  """
  Computes reach, gravity, betweenness, closeness, and straightness on a graph.
  |nodes|: graph representation; dictionary mapping node id's to |Node| objects
  |origins|: subset of nodes that will be used as sources of shortest path trees
  |compute_r|: compute reach?
  |compute_g|: compute gravity type index?
  |compute_b|: compute betweenness?
  |compute_c|: compute closeness?
  |compute_s|: compute straightness?
  |radius|: for each node, only consider other nodes that can be reached within
      this distance
  |network_radius|: use network radius or birds-eye radius?
  |beta|: parameter for gravity type index
  |measures_to_normalize|: a list of measures to normalize
  |accumulator_fields|: a list of cost attributes to accumulate
  """

  # Number of nodes in the graph
  N = len(nodes)
  O = len(origins)
  if O > N:
    raise Invalid_Parameters_Exception("size of origins exceeds size of nodes")
  elif O == 0:
    return

  # Preprocessing
  have_accumulations = len(accumulator_fields) > 0
  if have_accumulations:
    empty_accumulations = lambda: dict((field, 0.0) for field in
        accumulator_fields)
  have_locations = hasattr(nodes.values()[0], LOCATION)
  if compute_s and not have_locations:
    # We cannot compute straightness without node locations
    compute_s = False
  if compute_b:
    # Initialize betweenness values
    for id in nodes:
      setattr(nodes[id], BETWEENNESS, 0.0)

  # Initialize the sum of all node weights (normalization)
  sum_weights = 0.0

  # Computation
  progress = Progress_Bar(O, 1, STEP_4)
  for s in origins:
    if s not in nodes:
      continue
    weight_s = getattr(nodes[s], WEIGHT)
    if have_locations: location_s = getattr(nodes[s], LOCATION)

    sum_weights += weight_s

    # Initialize reach (weighted and unweighted) computation for |s|
    #     (normalization)
    reach_s = -1
    weighted_reach_s = -weight_s

    # Initialize measures
    if compute_g: gravity_s = 0.0
    if compute_b:
      P = {s: []} # Predecessors
      S = [] # Stack containing nodes in the order they are extended
      sigma = {s: 1.0} # Number of shortest paths from |s| to other nodes
      delta = {} # Dependency of |s| on other nodes
    if compute_c: d_sum_s = 0.0
    if compute_s: straightness_s = 0.0
    if have_accumulations:
      accumulations_s = {s: empty_accumulations()}

    d = {s: 0.0} # Shortest distance from |s| to other nodes
    # Queue for Dijkstra
    Q = [(0.0, s)] if network_radius else [(0.0, s, 0.0)]

    # If we use euclidean radius, make a list of all reachable nodes
    if not network_radius:
      reachable_s = set()
      for t in nodes:
        location_t = getattr(nodes[t], LOCATION)
        if dist(location_s, location_t) <= radius:
          reachable_s.add(t)

    # Dijkstra
    while Q and (True if network_radius else reachable_s):
      # Pop the closest node to |s| from |Q|
      if network_radius:
        d_sv, v = heappop(Q)
      else:
        d_sv, v, dist_sv = heappop(Q)
        if v in reachable_s:
          reachable_s.remove(v)
      weight_v = getattr(nodes[v], WEIGHT)
      if have_locations: location_v = getattr(nodes[v], LOCATION)

      compute = network_radius or dist_sv <= radius
      if compute:
        reach_s += 1
        weighted_reach_s += weight_v
        if d_sv > 0:
          if compute_g: gravity_s += weight_v * exp(-d_sv * beta)
          if compute_c: d_sum_s += weight_v * d_sv
          if compute_s: straightness_s += (weight_v *
              dist(location_s, location_v) / d_sv)
        if compute_b: S.append(v)

      for w, d_vw, accumulations_vw in getattr(nodes[v], NEIGHBORS):
        # s ~ ... ~ v ~ w
        d_sw = d_sv + d_vw
        if not network_radius:
            # Use Euclidean distance
            location_w = getattr(nodes[w], LOCATION)
            dist_sw = dist(location_s, location_w)

        if compute_b: b_refresh = False

        add_w_to_Q = False

        if not w in d: # Found a path from |s| to |w| for the first time
          if d_sw <= radius or not network_radius:
            add_w_to_Q = True
          d[w] = d_sw
          if compute_b: b_refresh = True

        elif lt_tol(d_sw, d[w]): # Found a better path from |s| to |w|
          if d_sw <= radius or not network_radius:
            if d[w] <= radius or not network_radius:
              longer_path_node = (d[w], w) if network_radius else (d[w], w,
                  dist_sw)
              Q.remove(longer_path_node)
              heapify(Q)
            add_w_to_Q = True
          d[w] = d_sw
          if compute_b: b_refresh = True

        if add_w_to_Q:
          new_node = (d_sw, w) if network_radius else (d_sw, w, dist_sw)
          heappush(Q, new_node)
          if have_accumulations:
            accumulations_s[w] = merge_maps(accumulations_s[v],
                dict(accumulations_vw), add)

        if compute_b:
          if b_refresh:
            sigma[w] = 0.0
            P[w] = []
          if eq_tol(d_sw, d[w]): # Count all shortest paths from |s| to |w|
            sigma[w] += sigma[v] # Update the number of shortest paths
            P[w].append(v) # |v| is a predecessor of |w|
            delta[v] = 0.0 # Recognize |v| as a predecessor

    if compute_r: setattr(nodes[s], REACH, weighted_reach_s)
    if compute_g: setattr(nodes[s], GRAVITY, gravity_s)
    if compute_b:
      while S: # Revisit nodes in reverse order of distance from |s|
        w = S.pop()
        delta_w = delta[w] if w in delta else 0.0 # Dependency of |s| on |w|
        for v in P[w]:
          weight_w = getattr(nodes[w], WEIGHT)
          delta[v] += sigma[v] / sigma[w] * (weight_w + delta_w)
        if w != s:
          between_w = getattr(nodes[w], BETWEENNESS)
          setattr(nodes[w], BETWEENNESS, between_w + delta_w)
    if compute_c: setattr(nodes[s], CLOSENESS, (1.0 / d_sum_s if d_sum_s > 0
        else 0.0))
    if compute_s: setattr(nodes[s], STRAIGHTNESS, straightness_s)

    nodes[s].reach = reach_s
    nodes[s].weighted_reach = weighted_reach_s

    if have_accumulations:
      total_accumulations_s = empty_accumulations()
      for v in accumulations_s:
        total_accumulations_s = merge_maps(total_accumulations_s,
            accumulations_s[v], add)
      for field in accumulator_fields:
        setattr(nodes[s], field, total_accumulations_s[field])

    progress.step()

  # Normalization
  if BETWEENNESS in measures_to_normalize and O < N:
      measures_to_normalize.remove(BETWEENNESS)
      AddWarning(WARNING_NO_BETWEENNESS_NORMALIZATION)
  if measures_to_normalize:
    norm_progress = Progress_Bar(O, 1, PROGRESS_NORMALIZATION)
    for s in origins:
      if s not in nodes:
        continue
      reach_s = nodes[s].reach
      weighted_reach_s = nodes[s].weighted_reach

      # Normalize reach
      if compute_r and REACH in measures_to_normalize:
        weight_s = getattr(nodes[s], WEIGHT)
        try: setattr(nodes[s], NORM_REACH, reach_s / (sum_weights - weight_s))
        except: setattr(nodes[s], NORM_REACH, 0.0)

      # Normalize gravity
      if compute_g and GRAVITY in measures_to_normalize:
        gravity_s = getattr(nodes[s], GRAVITY)
        try: setattr(nodes[s], NORM_GRAVITY, (exp(beta) * gravity_s /
            weighted_reach_s))
        except: setattr(nodes[s], NORM_GRAVITY, 0.0)

      # Normalize betweenness
      if compute_b and BETWEENNESS in measures_to_normalize:
        betweenness_s = getattr(nodes[s], BETWEENNESS)
        try: setattr(nodes[s], NORM_BETWEENNESS, (betweenness_s /
            (weighted_reach_s * (reach_s - 1))))
        except: setattr(nodes[s], NORM_BETWEENNESS, 0.0)

      # Normalize closeness
      if compute_c and CLOSENESS in measures_to_normalize:
        closeness_s = getattr(nodes[s], CLOSENESS)
        try: setattr(nodes[s], NORM_CLOSENESS, closeness_s * weighted_reach_s)
        except: setattr(nodes[s], NORM_CLOSENESS, 0.0)

      # Normalize straightness
      if compute_s and STRAIGHTNESS in measures_to_normalize:
        straightness_s = getattr(nodes[s], STRAIGHTNESS)
        try: setattr(nodes[s], NORM_STRAIGHTNESS, (straightness_s /
            weighted_reach_s))
        except: setattr(nodes[s], NORM_STRAIGHTNESS, 0.0)

      norm_progress.step()
def compute_centrality(nodes, origins, compute_r, compute_g, compute_b,
    compute_c, compute_s, radius, beta, measures_to_normalize,
    accumulator_fields):
  """
  Computes reach, gravity, betweenness, closeness, and straightness on a graph.
  |nodes|: graph representation; dictionary mapping node id's to |Node| objects
  |origins|: subset of nodes that will be used as sources of shortest path trees
  |compute_r|: compute reach?
  |compute_g|: compute gravity type index?
  |compute_b|: compute betweenness?
  |compute_c|: compute closeness?
  |compute_s|: compute straightness?
  |radius|: for each node, only consider other nodes that can be reached within
      this distance
  |beta|: parameter for gravity type index
  |measures_to_normalize|: a list of measures to normalize
  |accumulator_fields|: a list of cost attributes to accumulate
  """

  # Number of nodes in the graph
  N = len(nodes)
  O = len(origins)
  if N == 0 or O == 0:
    return

  # Preprocessing
  have_accumulations = len(accumulator_fields) > 0
  have_locations = hasattr(nodes.values()[0], LOCATION)
  if compute_s and not have_locations:
    # We cannot compute straightness without node locations
    compute_s = False
  if compute_b:
    # Initialize betweenness values
    for id in nodes:
      setattr(nodes[id], BETWEENNESS, 0.0)

  # Initialize the sum of all node weights (normalization)
  sum_weights = 0.0

  # Computation
  progress = Progress_Bar(O, 1, STEP_4)
  for s in origins:
    weight_s = getattr(nodes[s], WEIGHT)
    if have_locations: location_s = getattr(nodes[s], LOCATION)

    sum_weights += weight_s

    # Initialize reach (weighted and unweighted) computation for |s|
    #     (normalization)
    reach_s = -1
    weighted_reach_s = -weight_s

    # Initialize measures
    if compute_g: gravity_s = 0.0
    if compute_b:
      P = {s: []} # Predecessors
      S = [] # Stack containing nodes in the order they are extended
      sigma = {s: 1.0} # Number of shortest paths from |s| to other nodes
      delta = {} # Dependency of |s| on other nodes
    if compute_c: d_sum_s = 0.0
    if compute_s: straightness_s = 0.0
    if have_accumulations:
      empty_accumulations = lambda: dict((field, 0.0) for field in
          accumulator_fields)
      accumulations_s = {s: empty_accumulations()}

    d = {s: 0.0} # Shortest distance from |s| to other nodes
    Q = [(0.0, s)] # Queue for Dijkstra

    # Dijkstra
    while Q:
      # Pop the closest node to |s| from |Q|
      d_sv, v = heappop(Q)
      weight_v = getattr(nodes[v], WEIGHT)
      if have_locations: location_v = getattr(nodes[v], LOCATION)

      reach_s += 1
      weighted_reach_s += weight_v

      if d_sv > 0:
        if compute_g: gravity_s += weight_v * exp(-d_sv * beta)
        if compute_c: d_sum_s += weight_v * d_sv
        if compute_s: straightness_s += (weight_v * dist(location_s, location_v)
            / d_sv)
      if compute_b: S.append(v)

      for w, d_vw, accumulations_vw in getattr(nodes[v], NEIGHBORS):
        # s ~ ... ~ v ~ w
        d_sw = d_sv + d_vw
        if compute_b: b_refresh = False

        if not w in d: # Found a path from |s| to |w| for the first time
          if d_sw <= radius:
            heappush(Q, (d_sw, w)) # Add |w| to |Q|
            if have_accumulations:
              accumulations_s[w] = merge_maps(accumulations_s[v],
                  dict(accumulations_vw), add)
          d[w] = d_sw
          if compute_b: b_refresh = True

        elif lt_tol(d_sw, d[w]): # Found a better path from |s| to |w|
          if d_sw <= radius:
            if d[w] <= radius:
              Q.remove((d[w], w))
              heapify(Q)
            heappush(Q, (d_sw, w)) # Add |w| to |Q|
            if have_accumulations:
              accumulations_s[w] = merge_maps(accumulations_s[v],
                  dict(accumulations_vw), add)
          d[w] = d_sw
          if compute_b: b_refresh = True

        if compute_b:
          if b_refresh:
            sigma[w] = 0.0
            P[w] = []
          if eq_tol(d_sw, d[w]): # Count all shortest paths from |s| to |w|
            sigma[w] += sigma[v] # Update the number of shortest paths
            P[w].append(v) # |v| is a predecessor of |w|
            delta[v] = 0.0 # Recognize |v| as a predecessor

    if compute_r: setattr(nodes[s], REACH, weighted_reach_s)
    if compute_g: setattr(nodes[s], GRAVITY, gravity_s)
    if compute_b:
      while S: # Revisit nodes in reverse order of distance from |s|
        w = S.pop()
        delta_w = delta[w] if w in delta else 0.0 # Dependency of |s| on |w|
        for v in P[w]:
          weight_w = getattr(nodes[w], WEIGHT)
          delta[v] += sigma[v] / sigma[w] * (weight_w + delta_w)
        if w != s:
          between_w = getattr(nodes[w], BETWEENNESS)
          setattr(nodes[w], BETWEENNESS, between_w + delta_w)
    if compute_c: setattr(nodes[s], CLOSENESS, (1.0 / d_sum_s if d_sum_s > 0
        else 0.0))
    if compute_s: setattr(nodes[s], STRAIGHTNESS, straightness_s)

    nodes[s].reach = reach_s
    nodes[s].weighted_reach = weighted_reach_s

    if have_accumulations:
      total_accumulations_s = empty_accumulations()
      for v in accumulations_s:
        total_accumulations_s = merge_maps(total_accumulations_s,
            accumulations_s[v], add)
      for field in accumulator_fields:
        setattr(nodes[s], field, total_accumulations_s[field])

    progress.step()

  # Normalization
  if BETWEENNESS in measures_to_normalize and O < N:
      measures_to_normalize.remove(BETWEENNESS)
      AddWarning(WARNING_NO_BETWEENNESS_NORMALIZATION)
  if measures_to_normalize:
    norm_progress = Progress_Bar(O, 1, PROGRESS_NORMALIZATION)
    for s in origins:
      reach_s = nodes[s].reach
      weighted_reach_s = nodes[s].weighted_reach

      # Normalize reach
      if compute_r and REACH in measures_to_normalize:
        weight_s = getattr(nodes[s], WEIGHT)
        try: setattr(nodes[s], NORM_REACH, reach_s / (sum_weights - weight_s))
        except: setattr(nodes[s], NORM_REACH, 0.0)

      # Normalize gravity
      if compute_g and GRAVITY in measures_to_normalize:
        gravity_s = getattr(nodes[s], GRAVITY)
        try: setattr(nodes[s], NORM_GRAVITY, (exp(beta) * gravity_s /
            weighted_reach_s))
        except: setattr(nodes[s], NORM_GRAVITY, 0.0)

      # Normalize betweenness
      if compute_b and BETWEENNESS in measures_to_normalize:
        betweenness_s = getattr(nodes[s], BETWEENNESS)
        try: setattr(nodes[s], NORM_BETWEENNESS, (betweenness_s /
            (weighted_reach_s * (reach_s - 1))))
        except: setattr(nodes[s], NORM_BETWEENNESS, 0.0)

      # Normalize closeness
      if compute_c and CLOSENESS in measures_to_normalize:
        closeness_s = getattr(nodes[s], CLOSENESS)
        try: setattr(nodes[s], NORM_CLOSENESS, closeness_s * weighted_reach_s)
        except: setattr(nodes[s], NORM_CLOSENESS, 0.0)

      # Normalize straightness
      if compute_s and STRAIGHTNESS in measures_to_normalize:
        straightness_s = getattr(nodes[s], STRAIGHTNESS)
        try: setattr(nodes[s], NORM_STRAIGHTNESS, (straightness_s /
            weighted_reach_s))
        except: setattr(nodes[s], NORM_STRAIGHTNESS, 0.0)

      norm_progress.step()
示例#4
0
        AddMessage(STEP_1_FAILED)
        success = False

  # Step 2
  if success:
    AddMessage(STEP_2_STARTED)
    try:
      distance_field = trim("Total_%s" % inputs[IMPEDANCE_ATTRIBUTE])
      accumulator_fields = set([trim("Total_%s" % accumulator_attribute)
          for accumulator_attribute in inputs[ACCUMULATOR_ATTRIBUTES].split(";")
          if accumulator_attribute != "#"])
      # Graph representation: dictionary mapping node id's to Node objects
      nodes = {}
      # The number of rows in |adj_dbf|
      directed_edge_count = int(GetCount_management(adj_dbf).getOutput(0))
      graph_progress = Progress_Bar(directed_edge_count, 1, STEP_2)
      rows = UpdateCursor(adj_dbf)
      for row in rows:
        # Get neighboring nodes, and the distance between them
        origin_id = row.getValue(trim(ORIGIN_ID_FIELD_NAME))
        destination_id = row.getValue(trim(DESTINATION_ID_FIELD_NAME))
        distance = float(row.getValue(distance_field))
        # Make sure the nodes are recorded in the graph
        for id in [origin_id, destination_id]:
          if not id in nodes:
            nodes[id] = Node()
        # Make sure that the nodes are neighbors in the graph
        if origin_id != destination_id and distance >= 0:
          accumulations = {}
          for field in accumulator_fields:
            accumulations[field] = float(row.getValue(field))
def compute_adjacency_list(input_points, input_network, id_attribute,
    impedance_attribute, accumulator_attributes, search_radius, output_location,
    adj_dbf_name):
  """
  |input_points|: point shape file marking entity (e.g. building) locations
  |input_network|: street network in which |input_points| is located
  |id_attribute|: the name of attribute that distinguishes between input points
  |impedance_attribute|: distance between neighboring nodes will be based on
      this attribute
  |accumulator_attributes|: distance between neighboring nodes will also be
      recorded for these attributes
  |search_radius|: the maximum extent for centrality computation
  |output_location|: adjacency list dbf will be saved here
  |adj_dbf_name|: the name of the adjacency list dbf
  """

  # Number of points in |input_points|
  input_point_count = int(GetCount_management(input_points).getOutput(0))

  # Make a directory to store all auxiliary files
  auxiliary_dir = join(output_location, AUXILIARY_DIR_NAME)
  if not Exists(auxiliary_dir):
    mkdir(auxiliary_dir)

  # Record the edge and junction source names of |input_network|
  edge_feature = None
  junction_feature = None
  for source in Describe(input_network).sources:
    if source.sourceType == EDGE_FEATURE:
      edge_feature = source.name
    elif source.sourceType in JUNCTION_FEATURE:
      junction_feature = source.name
  if edge_feature == None:
    AddWarning(WARNING_NO_EDGE_FEATURE(input_network))
    raise Invalid_Input_Exception("Input Network")
  if junction_feature == None:
    AddWarning(WARNING_NO_JUNCTION_FEATURE(input_network))
    raise Invalid_Input_Exception("Input Network")

  # Calculate network locations if not already calculated
  test_input_point = UpdateCursor(input_points).next()
  locations_calculated = all(row_has_field(test_input_point, field)
      for field in NETWORK_LOCATION_FIELDS)
  if not locations_calculated:
    AddMessage(CALCULATE_LOCATIONS_STARTED)
    CalculateLocations_na(in_point_features=input_points,
        in_network_dataset=input_network,
        search_tolerance=SEARCH_TOLERANCE,
        search_criteria=("%s SHAPE; %s SHAPE;" %
            (junction_feature, edge_feature)),
        exclude_restricted_elements="INCLUDE")
    AddMessage(CALCULATE_LOCATIONS_FINISHED)

  # Calculate barrier cost per input point if not already calculated
  barrier_costs_calculated = row_has_field(test_input_point,
      trim(BARRIER_COST_FIELD))
  if not barrier_costs_calculated:
    AddMessage(BARRIER_COST_COMPUTATION_STARTED)
    # Add |BARRIER_COST_FIELD| column in |input_points|
    AddField_management(in_table=input_points,
        field_name=trim(BARRIER_COST_FIELD), field_type="DOUBLE",
        field_is_nullable="NON_NULLABLE")

    # Initialize a dictionary to store the frequencies of (SnapX, SnapY) values
    xy_count = {}
    # A method to retrieve a (SnapX, SnapY) pair for a row in |input_points|
    get_xy = lambda row: (row.getValue(trim("SnapX")),
        row.getValue(trim("SnapY")))

    barrier_pre_progress = Progress_Bar(input_point_count, 1,
        BARRIER_COST_PRE_PROCESSING)
    rows = UpdateCursor(input_points)
    for row in rows:
      snap_xy = get_xy(row)
      if snap_xy in xy_count:
        xy_count[snap_xy] += 1
      else:
        xy_count[snap_xy] = 1
      barrier_pre_progress.step()

    # Populate |BARRIER_COST_FIELD|, this will be used in OD matrix computation
    barrier_progress = Progress_Bar(input_point_count, 1,
        BARRIER_COST_COMPUTATION)
    rows = UpdateCursor(input_points)
    for row in rows:
      barrier_cost = BARRIER_COST / xy_count[get_xy(row)]
      row.setValue(trim(BARRIER_COST_FIELD), barrier_cost)
      rows.updateRow(row)
      barrier_progress.step()
    AddMessage(BARRIER_COST_COMPUTATION_FINISHED)

  # Necessary files
  od_cost_matrix_layer = join(auxiliary_dir, OD_COST_MATRIX_LAYER_NAME)
  od_cost_matrix_lines = join(od_cost_matrix_layer, OD_COST_MATRIX_LINES)
  temp_adj_dbf_name = "%s~.dbf" % adj_dbf_name[:-4]
  temp_adj_dbf = join(output_location, temp_adj_dbf_name)
  adj_dbf = join(output_location, adj_dbf_name)
  partial_adj_dbf = join(auxiliary_dir, PARTIAL_ADJACENCY_LIST_NAME)
  polygons = join(auxiliary_dir, POLYGONS_SHAPEFILE_NAME)
  raster = join(auxiliary_dir, RASTER_NAME)
  polygons_layer = join(auxiliary_dir, POLYGONS_LAYER_NAME)
  input_points_layer = join(auxiliary_dir, INPUT_POINTS_LAYER_NAME)

  # Make sure none of these files already exists
  for path in [od_cost_matrix_layer, temp_adj_dbf, adj_dbf, partial_adj_dbf,
      polygons, raster, polygons_layer, input_points_layer,
      od_cost_matrix_lines]:
    delete(path)

  # Cutoff radius for OD matrix computation
  cutoff_radius = 2 * BARRIER_COST + min(search_radius, BARRIER_COST / 2)

  # Compute OD matrix
  MakeODCostMatrixLayer_na(in_network_dataset=input_network,
      out_network_analysis_layer=od_cost_matrix_layer,
      impedance_attribute=impedance_attribute,
      default_cutoff=str(cutoff_radius),
      accumulate_attribute_name=accumulator_attributes,
      UTurn_policy="ALLOW_UTURNS", hierarchy="NO_HIERARCHY",
      output_path_shape="NO_LINES")

  # Determine raster cell size
  points_per_raster_cell = OD_MATRIX_ENTRIES / input_point_count
  raster_cell_count = max(1, input_point_count / points_per_raster_cell)
  input_points_extent = Describe(input_points).Extent
  raster_cell_area = (input_points_extent.width * input_points_extent.height /
      raster_cell_count)
  raster_cell_size = int(sqrt(raster_cell_area))

  # Construct |raster| from |input_points|
  PointToRaster_conversion(in_features=input_points,
      value_field=id_attribute, out_rasterdataset=raster,
      cell_assignment="MOST_FREQUENT", priority_field="NONE",
      cellsize=str(raster_cell_size))

  # Construct |polygons| from |raster|
  RasterToPolygon_conversion(in_raster=raster,
      out_polygon_features=polygons, simplify="NO_SIMPLIFY",
      raster_field="VALUE")

  # Export empty |od_cost_matrix_lines| to |temp_dbf| to start adjacency list
  TableToTable_conversion(in_rows=od_cost_matrix_lines,
      out_path=output_location, out_name=temp_adj_dbf_name)

  # Construct |polygons_layer| and |input_points_layer|
  for (feature, layer) in [(polygons, polygons_layer),
      (input_points, input_points_layer)]:
    MakeFeatureLayer_management(in_features=feature, out_layer=layer)

  def add_locations(sub_layer, field_mappings=""):
    """
    |sub_layer|: one of "Origins", "Destinations", "Barrier Points"
    |field_mappings|: field mappings in addition to those for "Name" and
        "CurbApproach"
    """
    AddLocations_na(in_network_analysis_layer=od_cost_matrix_layer,
        sub_layer=sub_layer, in_table=input_points_layer,
        field_mappings=("Name %s #; CurbApproach # 0; %s" %
            (id_attribute, field_mappings)),
        search_tolerance=SEARCH_TOLERANCE,
        search_criteria=("%s SHAPE; %s SHAPE;" %
            (junction_feature, edge_feature)),
        append="CLEAR", snap_to_position_along_network="SNAP",
        snap_offset=SNAP_OFFSET)

  # OD cost matrix destinations
  AddMessage(ADDING_DESTINATIONS_STARTED)
  SelectLayerByLocation_management(in_layer=input_points_layer)
  add_locations("Destinations")
  AddMessage(ADDING_DESTINATIONS_FINISHED)

  # OD cost matrix point barriers
  AddMessage(ADDING_BARRIERS_STARTED)
  add_locations("Point Barriers", ("FullEdge # 0; BarrierType # 2;"
      "Attr_%s %s #;" % (impedance_attribute, trim(BARRIER_COST_FIELD))))
  AddMessage(ADDING_BARRIERS_FINISHED)

  # Compute adjacency list, one raster cell at a time
  progress = Progress_Bar(raster_cell_count, 1, STEP_1)
  rows = UpdateCursor(polygons)
  for row in rows:
    # Select the current polygon
    SelectLayerByAttribute_management(in_layer_or_view=polygons_layer,
        selection_type="NEW_SELECTION", where_clause="FID = %s" % str(row.FID))

    # Origins
    SelectLayerByLocation_management(in_layer=input_points_layer,
        select_features=polygons_layer)
    add_locations("Origins")

    # Solve OD Cost matrix
    Solve_na(in_network_analysis_layer=od_cost_matrix_layer,
        ignore_invalids="SKIP")

    # Add origin and destination fields to the adjacency list dbf
    for (index, field) in [(0, ORIGIN_ID_FIELD_NAME),
        (1, DESTINATION_ID_FIELD_NAME)]:
      CalculateField_management(in_table=od_cost_matrix_lines,
          field=field, expression="!Name!.split(' - ')[%d]" % index,
          expression_type="PYTHON")

    # Record actual distance between neighboring nodes
    distance_field = "Total_%s" % impedance_attribute
    CalculateField_management(in_table=od_cost_matrix_lines,
        field=distance_field,
        expression="!%s! - 2 * %d" % (distance_field, BARRIER_COST),
        expression_type="PYTHON")

    # Append result to |temp_adj_dbf|
    TableToTable_conversion(in_rows=od_cost_matrix_lines,
        out_path=auxiliary_dir, out_name=PARTIAL_ADJACENCY_LIST_NAME)
    Append_management(inputs=partial_adj_dbf, target=temp_adj_dbf,
        schema_type="TEST")

    progress.step()

  # Copy data from |temp_adj_dbf| to |adj_dbf|
  Rename_management(in_data=temp_adj_dbf, out_data=adj_dbf)

  # Clean up
  for path in [od_cost_matrix_layer, partial_adj_dbf, polygons, raster,
      polygons_layer, input_points_layer, auxiliary_dir]:
    delete(path)
        AddMessage(STEP_1_FAILED)
        success = False

  # Step 2
  if success:
    AddMessage(STEP_2_STARTED)
    try:
      distance_field = trim("Total_%s" % inputs[IMPEDANCE_ATTRIBUTE])
      accumulator_fields = set([trim("Total_%s" % accumulator_attribute)
          for accumulator_attribute in inputs[ACCUMULATOR_ATTRIBUTES].split(";")
          if accumulator_attribute != "#"])
      # Graph representation: dictionary mapping node id's to Node objects
      nodes = {}
      # The number of rows in |adj_dbf|
      directed_edge_count = int(GetCount_management(adj_dbf).getOutput(0))
      graph_progress = Progress_Bar(directed_edge_count, 1, STEP_2)
      rows = UpdateCursor(adj_dbf)
      for row in rows:
        # Get neighboring nodes, and the distance between them
        origin_id = row.getValue(trim(ORIGIN_ID_FIELD_NAME))
        destination_id = row.getValue(trim(DESTINATION_ID_FIELD_NAME))
        distance = float(row.getValue(distance_field))
        # Make sure the nodes are recorded in the graph
        for id in [origin_id, destination_id]:
          if not id in nodes:
            nodes[id] = Node()
        # Make sure that the nodes are neighbors in the graph
        if origin_id != destination_id and distance >= 0:
          accumulations = {}
          for field in accumulator_fields:
            accumulations[field] = float(row.getValue(field))