def calculate_projection_to_nearest_row(codes, rows): # Copy rows so we can add temporary fields without modifying original rows = copy.copy(rows) CodeWithProjection = namedtuple('CodeWithProjection', 'code projection') codes_with_projections = [] # Order codes and rows from left to right codes = sorted(codes, key=lambda c: c.field_position[0]) rows = sorted(rows, key=lambda r: r.center_field_position[0]) for row in rows: row.ordered_items = [row.start_code, row.end_code] for code in codes: rows_with_distance_to_code = [(row, abs(row.center_field_position[0] - code.field_position[0])) for row in rows] sorted_rows_with_distance_to_code = sorted(rows_with_distance_to_code, key=lambda r: r[1]) closest_rows_to_code = [row[0] for row in sorted_rows_with_distance_to_code[:4]] min_distance = sys.float_info.max closest_row = None closest_after_in_closest_row = None for row in closest_rows_to_code: closest_beneath = None closest_after = None for item in row.ordered_items: if code.field_position[1] > item.field_position[1]: closest_beneath = item else: closest_after = item break if closest_beneath is None or closest_after is None: continue # not in this row for sure distance_to_code, _ = lateral_and_projection_distance_2d(code.field_position, closest_beneath.field_position, closest_after.field_position) distance_to_code = abs(distance_to_code) if distance_to_code < min_distance: min_distance = distance_to_code closest_after_in_closest_row = closest_after closest_row = row if closest_row is not None and min_distance < 3: # TODO remove hard-coded value _, row_projection = lateral_and_projection_distance_2d(code.field_position, row.start_code.field_position, row.end_code.field_position) closest_item_idx = closest_row.ordered_items.index(closest_after_in_closest_row) closest_row.ordered_items.insert(closest_item_idx, code) code.row = closest_row.number codes_with_projections.append(CodeWithProjection(code, row_projection)) else: code.row = -1 print "Couldn't find a row for code {}. Closest row is {} meters away.".format(code.name, min_distance) return codes_with_projections
def filter_plants_by_segment_part(self, segment_part): possible_plants = segment_part.possible_plants for plant in possible_plants: lateral_distance, projection_distance = lateral_and_projection_distance_2d(plant['position'], segment_part.start.position, segment_part.end.position) plant['lateral'] = lateral_distance plant['projection'] = projection_distance # Order from start to end code possible_plants = sorted(possible_plants, key=lambda p: p['projection']) # Throw out any that are too close to or behind start/end code if segment_part.start.type == 'GroupCode': closest_at_start = self.closest_group_code_spacing elif segment_part.start.type == 'RowCode': closest_at_start = self.closest_row_code_spacing else: closest_at_start = self.closest_plant_spacing if segment_part.start.type == 'GroupCode': closest_at_end = segment_part.length - self.closest_group_code_spacing elif segment_part.start.type == 'RowCode': closest_at_end = segment_part.length - self.closest_row_code_spacing else: closest_at_end = segment_part.length - self.closest_plant_spacing filtered_plants = [p for p in possible_plants if p['projection'] >= closest_at_start and p['projection'] <= closest_at_end] return filtered_plants
def split_segment_into_parts(self, segment_part, forward_plant, reverse_plant): if not forward_plant or not reverse_plant: return [] # Can't split up any more. # First change reverse plant projection to be in the forward direction to make it consistent. _, reverse_plant.projection = lateral_and_projection_distance_2d(reverse_plant.position, segment_part.start.position, segment_part.end.position) projection_difference = reverse_plant.projection - forward_plant.projection selected_plants = [] if abs(projection_difference) < 0.0001: # Segments split on same plant so it doesn't matter which one we choose. selected_plants = [forward_plant] elif projection_difference > self.closest_plant_spacing: # Normal case where forward/reverse don't overlap. We should now have 3 segments. selected_plants = [forward_plant, reverse_plant] else: # Forward/reverse overlap. Need to decide which one to use. if forward_plant.type == 'CreatedPlant' and reverse_plant.type == 'CreatedPlant': avg_position = np.mean([forward_plant.position, reverse_plant.position], axis=0) avg_plant = CreatedPlant(name='plant', position=avg_position, zone=forward_plant.zone) avg_plant.projection = np.mean([forward_plant.projection, reverse_plant.projection], axis=0) selected_plants = [avg_plant] elif forward_plant.type == 'Plant' and reverse_plant.type == 'Plant': if forward_plant.penalty < reverse_plant.penalty: selected_plants = [forward_plant] else: selected_plants = [reverse_plant] elif forward_plant.type == 'Plant': selected_plants = [forward_plant] elif reverse_plant.type == 'Plant': selected_plants = [reverse_plant] else: assert(False) if len(selected_plants) == 0: return [] elif len(selected_plants) == 1: selected_plant = selected_plants[0] first_subpart = SegmentPart(start=segment_part.start, end=selected_plant) second_subpart = SegmentPart(start=selected_plant, end=segment_part.end) before_plants, after_plants = self.split_possible_plants_by_projections(segment_part.possible_plants, [selected_plant.projection]) first_subpart.possible_plants = before_plants second_subpart.possible_plants = after_plants return [first_subpart, second_subpart] elif len(selected_plants) == 2: first_subpart = SegmentPart(start=segment_part.start, end=selected_plants[0]) second_subpart = SegmentPart(start=selected_plants[0], end=selected_plants[1]) third_subpart = SegmentPart(start=selected_plants[1], end=segment_part.end) first_subpart.possible_plants, second_subpart.possible_plants, third_subpart.possible_plants = \ self.split_possible_plants_by_projections(segment_part.possible_plants, [selected_plants[0].projection, selected_plants[1].projection]) return [first_subpart, second_subpart, third_subpart] else: assert(False)