File size: 7,057 Bytes
5f6bdbf
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3bd69a8
 
5f6bdbf
 
 
 
 
 
 
 
 
3bd69a8
 
5f6bdbf
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3bd69a8
 
5f6bdbf
 
 
 
 
 
 
3bd69a8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5f6bdbf
 
 
 
 
 
 
 
3bd69a8
5f6bdbf
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3bd69a8
 
 
 
 
 
 
 
5f6bdbf
3bd69a8
 
 
 
 
 
5f6bdbf
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3bd69a8
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
"""
Algorithm configuration settings and constants.

Contains all configurable parameters for the land redistribution algorithm,
organized into dataclasses for type safety and clarity.
"""

from dataclasses import dataclass, field
from typing import Tuple


@dataclass(frozen=True)
class RoadSettings:
    """Road and transportation infrastructure settings (TCVN standards)."""
    
    # Road widths (meters)
    main_width: float = 20.0       # Main road
    internal_width: float = 10.0   # Internal road (reduced from 15.0 to allow better efficiency)
    sidewalk_width: float = 4.0    # Sidewalk each side (includes utility trench)
    turning_radius: float = 15.0   # Corner chamfer radius for intersections


@dataclass(frozen=True)
class SubdivisionSettings:
    """Block and lot subdivision settings."""
    
    # Land allocation
    service_area_ratio: float = 0.10    # 10% for infrastructure
    min_block_area: float = 400.0       # Minimum block area (reduced from 5000.0 to 400.0)
    
    # Lot dimensions (industrial)
    min_lot_width: float = 20.0         # Minimum lot frontage (m)
    max_lot_width: float = 80.0         # Maximum lot frontage (m)
    target_lot_width: float = 40.0      # Target lot width (m)
    
    # Legal/Construction (TCVN)
    setback_distance: float = 6.0       # Building setback from road (m)
    fire_safety_gap: float = 4.0        # Fire safety gap between buildings (m)
    
    # Solver
    solver_time_limit: float = 0.5      # OR-Tools time limit per block (seconds)


@dataclass(frozen=True)
class InfrastructureSettings:
    """Infrastructure planning settings."""
    
    # Electrical
    transformer_radius: float = 300.0   # Effective service radius (m)
    lots_per_transformer: int = 15      # Approximate lots per transformer
    
    # Network
    loop_redundancy_ratio: float = 0.15 # 15% extra edges for loop network safety
    max_connection_distance: float = 500.0  # Max distance for lot connections (m)
    
    # Drainage
    drainage_arrow_length: float = 30.0 # Arrow length for visualization (m)


@dataclass(frozen=True)
class OptimizationSettings:
    """NSGA-II genetic algorithm settings."""
    
    # Population
    population_size: int = 30
    generations: int = 15
    
    # Crossover/Mutation
    crossover_probability: float = 0.7
    mutation_probability: float = 0.3
    eta: float = 20.0  # Distribution index for SBX crossover
    
    # Gene bounds
    # Gene bounds - Increased to create larger, more usable blocks
    spacing_bounds: Tuple[float, float] = (50.0, 150.0)
    angle_bounds: Tuple[float, float] = (0.0, 90.0)
    
    # Block quality thresholds
    good_block_ratio: float = 0.65      # Ratio for residential/commercial
    fragmented_block_ratio: float = 0.1 # Below this = too small


@dataclass(frozen=True)
class AestheticSettings:
    """Shape quality thresholds for aesthetic optimization (from Beauti_mode)."""
    
    # Rectangularity: area / OBB area (1.0 = perfect rectangle)
    # Relaxed to 0.65 to accept trapezoids from Voronoi slicing
    min_rectangularity: float = 0.65
    
    # Aspect ratio: length / width (lower = more square)
    max_aspect_ratio: float = 4.0
    
    # Minimum lot area to avoid tiny fragments (m²)
    # Relaxed from 1000.0 to 250.0 to accept standard industrial/residential lots
    min_lot_area: float = 250.0
    
    # OR-Tools deviation penalty weight (higher = more uniform lots)
    deviation_penalty_weight: float = 50.0
    
    # Enable leftover management (convert poor lots to green space)
    enable_leftover_management: bool = True


@dataclass
class AlgorithmSettings:
    """Complete algorithm configuration."""
    
    road: RoadSettings = field(default_factory=RoadSettings)
    subdivision: SubdivisionSettings = field(default_factory=SubdivisionSettings)
    infrastructure: InfrastructureSettings = field(default_factory=InfrastructureSettings)
    optimization: OptimizationSettings = field(default_factory=OptimizationSettings)
    aesthetic: AestheticSettings = field(default_factory=AestheticSettings)
    
    # Random seed for reproducibility
    random_seed: int = 42
    
    @classmethod
    def from_dict(cls, config: dict) -> 'AlgorithmSettings':
        """Create settings from API config dictionary."""
        settings = cls()
        
        # Map API config to internal settings
        if 'min_lot_width' in config:
            settings = cls(
                subdivision=SubdivisionSettings(
                    min_lot_width=config.get('min_lot_width', 20.0),
                    max_lot_width=config.get('max_lot_width', 80.0),
                    target_lot_width=config.get('target_lot_width', 40.0),
                    solver_time_limit=config.get('ortools_time_limit', 0.5),
                ),
                optimization=OptimizationSettings(
                    population_size=config.get('population_size', 30),
                    generations=config.get('generations', 15),
                    spacing_bounds=(
                        config.get('spacing_min', 50.0),
                        config.get('spacing_max', 150.0)
                    ),
                    angle_bounds=(
                        config.get('angle_min', 0.0),
                        config.get('angle_max', 90.0)
                    ),
                ),
                road=RoadSettings(
                    main_width=DEFAULT_SETTINGS.road.main_width,
                    internal_width=config.get('road_width', DEFAULT_SETTINGS.road.internal_width),
                    sidewalk_width=DEFAULT_SETTINGS.road.sidewalk_width,
                    turning_radius=DEFAULT_SETTINGS.road.turning_radius
                )
            )
        
        return settings


# Default settings instance
DEFAULT_SETTINGS = AlgorithmSettings()


# Convenience accessors for backward compatibility
ROAD_MAIN_WIDTH = DEFAULT_SETTINGS.road.main_width
ROAD_INTERNAL_WIDTH = DEFAULT_SETTINGS.road.internal_width
SIDEWALK_WIDTH = DEFAULT_SETTINGS.road.sidewalk_width
TURNING_RADIUS = DEFAULT_SETTINGS.road.turning_radius
SERVICE_AREA_RATIO = DEFAULT_SETTINGS.subdivision.service_area_ratio
MIN_BLOCK_AREA = DEFAULT_SETTINGS.subdivision.min_block_area
MIN_LOT_WIDTH = DEFAULT_SETTINGS.subdivision.min_lot_width
MAX_LOT_WIDTH = DEFAULT_SETTINGS.subdivision.max_lot_width
TARGET_LOT_WIDTH = DEFAULT_SETTINGS.subdivision.target_lot_width
SETBACK_DISTANCE = DEFAULT_SETTINGS.subdivision.setback_distance
FIRE_SAFETY_GAP = DEFAULT_SETTINGS.subdivision.fire_safety_gap
SOLVER_TIME_LIMIT = DEFAULT_SETTINGS.subdivision.solver_time_limit
TRANSFORMER_RADIUS = DEFAULT_SETTINGS.infrastructure.transformer_radius

# Aesthetic thresholds (from Beauti_mode)
MIN_RECTANGULARITY = DEFAULT_SETTINGS.aesthetic.min_rectangularity
MAX_ASPECT_RATIO = DEFAULT_SETTINGS.aesthetic.max_aspect_ratio
MIN_LOT_AREA = DEFAULT_SETTINGS.aesthetic.min_lot_area
DEVIATION_PENALTY_WEIGHT = DEFAULT_SETTINGS.aesthetic.deviation_penalty_weight
ENABLE_LEFTOVER_MANAGEMENT = DEFAULT_SETTINGS.aesthetic.enable_leftover_management