def _write_boat_area(pier, stg_manager, coords_transform: co.Transformation): if len(pier.nodes) < 3: return # Guess a possible position for realistic boat placement linear_ring = shg.LinearRing(pier.nodes) centroid = linear_ring.centroid # Simplify ring = linear_ring.convex_hull.buffer( 40, cap_style=CAP_STYLE.square, join_style=JOIN_STYLE.bevel).simplify(20) for p in ring.exterior.coords: line_coords = [[centroid.x, centroid.y], p] target_vector = shg.LineString(line_coords) coords = linear_ring.coords for i in range(len(coords) - 1): segment = LineString(coords[i:i + 2]) if segment.length > 20 and segment.intersects(target_vector): direction = math.degrees( math.atan2(segment.coords[0][0] - segment.coords[1][0], segment.coords[0][1] - segment.coords[1][1])) parallel = segment.parallel_offset(10, 'right') boat_position = parallel.interpolate(segment.length / 2) try: pos_global = coords_transform.to_global( (boat_position.x, boat_position.y)) _write_model(segment.length, stg_manager, pos_global, direction, pier.elevation) except AttributeError as reason: logging.error(reason)
def process_details(coords_transform: co.Transformation, lit_areas: Optional[List[shg.Polygon]], fg_elev: utilities.FGElev, file_lock: mp.Lock = None) -> None: stats = utilities.Stats() lmin, lmax = parameters.get_extent_local(coords_transform) clusters = ClusterContainer(lmin, lmax) # piers the_piers = piers.process_osm_piers(coords_transform) logging.info("number of piers: %i", len(the_piers)) for pier in the_piers: clusters.append(pier.anchor, pier, stats) for pier in the_piers: pier.calc_elevation(fg_elev) # platforms the_platforms = platforms.process_osm_platform(coords_transform) logging.info("number of platforms: %i", len(the_platforms)) for platform in the_platforms: clusters.append(platform.anchor, platform, stats) # -- initialize STGManager path_to_output = parameters.get_output_path() stg_manager = stg_io2.STGManager(path_to_output, stg_io2.SceneryType.details, OUR_MAGIC, parameters.PREFIX) for cl in clusters: if cl.objects: cluster_center_global = co.Vec2d( coords_transform.to_global(cl.center)) ac_file_name = "%sd%i%i.ac" % (parameters.PREFIX, cl.grid_index.ix, cl.grid_index.iy) ac = ac3d.File(stats=stats) obj = ac.new_object('details', 'Textures/Terrain/asphalt.png', default_mat_idx=mat.Material.unlit.value) for detail in cl.objects[:]: if isinstance(detail, piers.Pier): detail.write(obj, cl.center) else: detail.write(fg_elev, obj, cl.center) path = stg_manager.add_object_static( ac_file_name, cluster_center_global, 0, 0, parameters.get_cluster_dimension_radius()) file_name = os.path.join(path, ac_file_name) f = open(file_name, 'w') f.write(str(ac)) f.close() piers.write_boats(stg_manager, the_piers, coords_transform) # -- write stg stg_manager.write(file_lock) # trigger processing of pylon related details _process_pylon_details(coords_transform, lit_areas, fg_elev, stg_manager, lmin, lmax, file_lock)
def match_local_coords_with_global_nodes( local_list: List[Tuple[float, float]], ref_list: List[int], all_nodes: Dict[int, op.Node], coords_transform: co.Transformation, osm_id: int, create_node: bool = False) -> List[int]: """Given a set of coordinates in local space find matching Node objects in global space. Matching is using a bit of tolerance (cf. parameter), which should be enough to account for conversion precision resp. float precision. If a node cannot be matched: if parameter create_node is False, then a ValueError is thrown - else a new Node is created and added to the all_nodes dict. """ matched_nodes = list() nodes_local = dict( ) # key is osm_id from Node, value is Tuple[float, float] for ref in ref_list: node = all_nodes[ref] nodes_local[node.osm_id] = coords_transform.to_local( (node.lon, node.lat)) for local in local_list: closest_distance = 999999 found_key = -1 for key, node_local in nodes_local.items(): distance = co.calc_distance_local(local[0], local[1], node_local[0], node_local[1]) if distance < closest_distance: closest_distance = distance if distance < parameters.TOLERANCE_MATCH_NODE: found_key = key break if found_key < 0: if create_node: lon, lat = coords_transform.to_global(local) new_node = op.Node( op.get_next_pseudo_osm_id( op.OSMFeatureType.building_relation), lat, lon) all_nodes[new_node.osm_id] = new_node matched_nodes.append(new_node.osm_id) else: raise ValueError( 'No match for parent with osm_id = %d. Closest: %f' % (osm_id, closest_distance)) else: matched_nodes.append(found_key) return matched_nodes
def _write_boat_line(pier, stg_manager, coords_transform: co.Transformation): line_string = LineString(pier.nodes) right_line = line_string.parallel_offset(4, 'left', resolution=8, join_style=1, mitre_limit=10.0) if isinstance(right_line, LineString): # FIXME: what to do else? coords = right_line.coords for i in range(len(coords) - 1): segment = LineString(coords[i:i + 2]) boat_position = segment.interpolate(segment.length / 2) try: pos_global = coords_transform.to_global( (boat_position.x, boat_position.y)) direction = math.degrees( math.atan2(segment.coords[0][0] - segment.coords[1][0], segment.coords[0][1] - segment.coords[1][1])) if segment.length > 5: _write_model(segment.length, stg_manager, pos_global, direction, pier.elevation) except AttributeError as reason: logging.error(reason)
def _create_pseudo_stg_entries_for_exclude_areas( areas: Optional[List[List[Tuple[float, float]]]], transform: Transformation) -> List[STGEntry]: """Create a list of faked STGEntries for exclude areas. We cannot control the data quality of the user provided input, so no recovery on error -> fail fast. """ if areas is None or len(areas) == 0: return list() faked_entries = list() for i, list_of_tuples in enumerate(areas): my_coordinates = list() for lon_lat in list_of_tuples: x, y = transform.to_local((lon_lat[0], lon_lat[1])) my_coordinates.append((x, y)) if len(my_coordinates) >= 3: my_polygon = shg.Polygon(my_coordinates) if my_polygon.is_valid and not my_polygon.is_empty: lon, lat = transform.to_global( (my_polygon.centroid.x, my_polygon.centroid.y)) my_entry = STGEntry(STGVerbType.object_static.name, 'exclude area', 'fake', lon, lat, 0, 0) my_entry.convex_hull = my_polygon faked_entries.append(my_entry) else: raise ValueError( 'Resulting exclude area polygon is not valid or empty: Entry: %i', i + 1) else: raise ValueError( 'There must be at least 3 coordinate tuples per exclude area polygon. Entry: %i', i + 1) logging.info( 'Added %i fake static STGEntries for OVERLAP_CHECK_EXCLUDE_AREAS', len(faked_entries)) return faked_entries