import random from .network import compute_mst_fiber_paths, compute_network_summary GRID_SIZE = 5 SPACING = 15 # Distance between objects def generate_com_con_city_data(lat, long): """ Generate a digital twin for a real-world city (e.g., Concepciรณn). Returns towers, fiber paths, wifi hotspots, and a summary. """ random.seed(f"{lat},{long}") center_x = lat center_z = long towers = generate_towers(center_x, center_z) fiber_paths = compute_mst_fiber_paths(towers) wifi_hotspots = generate_wifi_hotspots(center_x, center_z) summary = compute_network_summary(towers, fiber_paths, wifi_hotspots) return { 'towers': towers, 'fiber_paths': fiber_paths, 'wifi_hotspots': wifi_hotspots, 'network_summary': summary, } def generate_towers(center_x, center_z, mode="streets"): """ Generate towers either in a 'grid' or at realistic 'streets' (mocked). mode: "grid" | "streets" """ if mode == "streets": return generate_street_corner_towers(center_x, center_z) else: return generate_grid_towers(center_x, center_z) import osmnx as ox def generate_street_corner_towers(center_x, center_z, min_towers=10): """ Get real intersections from OSM and convert them to local x/z positions relative to center_x / center_z (in meters). Fallbacks to mocked layout if needed. """ print("๐Ÿ“ Starting generate_street_corner_towers()") print(f"โ†’ center_x: {center_x}, center_z: {center_z}") point = (center_x, center_z) print(f"โ†’ Using real lat/lon: {point}") try: for dist in [100, 200, 500, 1000]: print(f"๐Ÿ›ฐ๏ธ Trying OSM download at radius: {dist} meters...") G = ox.graph_from_point(point, dist=dist, network_type='all') G_undirected = G.to_undirected() degrees = dict(G_undirected.degree()) intersections = [n for n, d in degrees.items() if d >= 3] print(f" โœ… Found {len(intersections)} valid intersections.") if len(intersections) >= min_towers: break else: raise ValueError("No sufficient intersections found.") nodes, _ = ox.graph_to_gdfs(G) origin_lon = nodes.loc[intersections]['x'].mean() origin_lat = nodes.loc[intersections]['y'].mean() print(f"๐Ÿ“Œ Using origin_lon: {origin_lon:.6f}, origin_lat: {origin_lat:.6f} for local projection") def latlon_to_sim(lon, lat): dx = (lon - origin_lon) * 111320 dz = (lat - origin_lat) * 110540 return center_x + dx, center_z + dz towers = [] for i, node_id in enumerate(intersections): row = nodes.loc[node_id] x_sim, z_sim = latlon_to_sim(row['x'], row['y']) print(f" ๐Ÿ—ผ Tower #{i+1} at sim position: x={x_sim:.2f}, z={z_sim:.2f}") towers.append(make_tower(x_sim, z_sim, i + 1)) print(f"โœ… Done. Total towers returned: {len(towers)}\n") return towers except Exception as e: print(f"โŒ OSM tower generation failed: {e}") print("โš ๏ธ Falling back to mocked tower layout.") # Return 3x3 fixed grid as fallback offsets = [(-30, -30), (-30, 0), (-30, 30), (0, -30), (0, 0), (0, 30), (30, -30), (30, 0), (30, 30)] towers = [] for i, (dx, dz) in enumerate(offsets): x = center_x + dx z = center_z + dz towers.append(make_tower(x, z, i + 1)) print(f"โœ… Fallback returned {len(towers)} towers.\n") return towers def generate_grid_towers(center_x, center_z): """Generates a 5ร—5 grid of towers around the city center.""" towers = [] for i in range(GRID_SIZE): for j in range(GRID_SIZE): x = center_x + (i - GRID_SIZE // 2) * SPACING z = center_z + (j - GRID_SIZE // 2) * SPACING towers.append({ 'id': len(towers) + 1, 'status': 'Active' if random.random() > 0.2 else 'Inactive', 'position_x': x, 'position_y': 0, 'position_z': z, 'height': random.randint(40, 60), 'range': random.randint(500, 1000), 'color': '#ff4500' }) return towers def generate_wifi_hotspots(center_x, center_z): """Places 10 Wi-Fi hotspots randomly around the city center.""" hotspots = [] bound = SPACING * GRID_SIZE / 2 for i in range(10): x = center_x + random.uniform(-bound, bound) z = center_z + random.uniform(-bound, bound) hotspots.append({ 'id': i + 1, 'position_x': x, 'position_y': 1.5, 'position_z': z, 'status': 'Online' if random.random() > 0.2 else 'Offline', 'radius': random.randint(1, 3), 'color': '#32cd32' }) return hotspots def make_tower(x, z, id): return { 'id': id, 'status': 'Active', 'position_x': x, 'position_y': 0, 'position_z': z, 'height': 50, 'range': 1000, 'color': '#ff4500' }