File size: 7,544 Bytes
b010f1b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
"""
Core domain models for REMB Optimization Engine
"""
from dataclasses import dataclass, field
from typing import List, Optional, Dict, Any
from enum import Enum
from shapely.geometry import Polygon, MultiPolygon, LineString, MultiLineString, Point
import uuid


class PlotType(str, Enum):
    """Types of plots in industrial estate"""
    INDUSTRIAL = "industrial"
    GREEN_SPACE = "green_space"
    ROAD = "road"
    UTILITY = "utility"
    BUFFER = "buffer"


class ConstraintType(str, Enum):
    """Types of constraints"""
    SETBACK = "setback"
    FIRE_SAFETY = "fire_safety"
    WATERWAY = "waterway"
    HAZARD_ZONE = "hazard_zone"
    NO_BUILD = "no_build"


@dataclass
class Constraint:
    """Spatial constraint"""
    type: ConstraintType
    geometry: Polygon
    buffer_distance_m: float
    description: str
    is_hard: bool = True  # Hard constraint vs soft constraint


@dataclass
class SiteBoundary:
    """Site boundary representation"""
    id: str = field(default_factory=lambda: str(uuid.uuid4()))
    geometry: Polygon = None
    area_sqm: float = 0.0
    constraints: List[Constraint] = field(default_factory=list)
    buildable_area_sqm: float = 0.0
    metadata: Dict[str, Any] = field(default_factory=dict)
    
    def calculate_buildable_area(self) -> float:
        """Calculate buildable area after applying constraints"""
        buildable = self.geometry
        for constraint in self.constraints:
            if constraint.is_hard:
                buildable = buildable.difference(constraint.geometry)
        self.buildable_area_sqm = buildable.area
        return self.buildable_area_sqm


@dataclass
class Plot:
    """Industrial plot representation"""
    id: str = field(default_factory=lambda: str(uuid.uuid4()))
    geometry: Polygon = None
    area_sqm: float = 0.0
    type: PlotType = PlotType.INDUSTRIAL
    frontage_m: float = 0.0
    width_m: float = 0.0
    depth_m: float = 0.0
    has_road_access: bool = False
    orientation_degrees: float = 0.0  # 0-360 degrees
    metadata: Dict[str, Any] = field(default_factory=dict)


@dataclass
class RoadNetwork:
    """Road network representation"""
    primary_roads: MultiLineString = None
    secondary_roads: MultiLineString = None
    tertiary_roads: MultiLineString = None
    total_length_m: float = 0.0
    total_area_sqm: float = 0.0
    
    def calculate_total_length(self) -> float:
        """Calculate total road network length"""
        length = 0.0
        if self.primary_roads:
            length += self.primary_roads.length
        if self.secondary_roads:
            length += self.secondary_roads.length
        if self.tertiary_roads:
            length += self.tertiary_roads.length
        self.total_length_m = length
        return length


@dataclass
class LayoutMetrics:
    """Metrics for evaluating a layout"""
    total_area_sqm: float = 0.0
    sellable_area_sqm: float = 0.0
    green_space_area_sqm: float = 0.0
    road_area_sqm: float = 0.0
    utility_area_sqm: float = 0.0
    
    # Ratios
    sellable_ratio: float = 0.0  # Sellable / Total
    green_space_ratio: float = 0.0  # Green / Total
    road_ratio: float = 0.0  # Road / Total
    
    # Compliance
    far_value: float = 0.0
    is_compliant: bool = False
    compliance_violations: List[str] = field(default_factory=list)
    
    # Efficiency
    road_efficiency: float = 0.0  # Lower is better (less road per sellable area)
    num_plots: int = 0
    avg_plot_size_sqm: float = 0.0
    
    def calculate_ratios(self):
        """Calculate all ratios"""
        if self.total_area_sqm > 0:
            self.sellable_ratio = self.sellable_area_sqm / self.total_area_sqm
            self.green_space_ratio = self.green_space_area_sqm / self.total_area_sqm
            self.road_ratio = self.road_area_sqm / self.total_area_sqm
            
            if self.sellable_area_sqm > 0:
                self.road_efficiency = self.road_area_sqm / self.sellable_area_sqm


@dataclass
class Layout:
    """Complete industrial estate layout"""
    id: str = field(default_factory=lambda: str(uuid.uuid4()))
    site_boundary: SiteBoundary = None
    plots: List[Plot] = field(default_factory=list)
    road_network: RoadNetwork = None
    metrics: LayoutMetrics = field(default_factory=LayoutMetrics)
    
    # Optimization metadata
    generation: int = 0
    fitness_scores: Dict[str, float] = field(default_factory=dict)
    pareto_rank: int = 0
    
    def calculate_metrics(self) -> LayoutMetrics:
        """Calculate all layout metrics"""
        self.metrics = LayoutMetrics()
        self.metrics.total_area_sqm = self.site_boundary.area_sqm
        
        # Calculate areas by type
        for plot in self.plots:
            if plot.type == PlotType.INDUSTRIAL:
                self.metrics.sellable_area_sqm += plot.area_sqm
            elif plot.type == PlotType.GREEN_SPACE:
                self.metrics.green_space_area_sqm += plot.area_sqm
            elif plot.type == PlotType.UTILITY:
                self.metrics.utility_area_sqm += plot.area_sqm
        
        # Road area
        if self.road_network:
            self.metrics.road_area_sqm = self.road_network.total_area_sqm
        
        # Calculate ratios
        self.metrics.num_plots = len([p for p in self.plots if p.type == PlotType.INDUSTRIAL])
        if self.metrics.num_plots > 0:
            self.metrics.avg_plot_size_sqm = self.metrics.sellable_area_sqm / self.metrics.num_plots
        
        self.metrics.calculate_ratios()
        
        return self.metrics


@dataclass
class ParetoFront:
    """Collection of Pareto-optimal solutions"""
    layouts: List[Layout] = field(default_factory=list)
    optimization_id: str = field(default_factory=lambda: str(uuid.uuid4()))
    generation_time_seconds: float = 0.0
    
    def get_max_sellable_layout(self) -> Optional[Layout]:
        """Get layout with maximum sellable area"""
        if not self.layouts:
            return None
        return max(self.layouts, key=lambda l: l.metrics.sellable_area_sqm)
    
    def get_max_green_layout(self) -> Optional[Layout]:
        """Get layout with maximum green space"""
        if not self.layouts:
            return None
        return max(self.layouts, key=lambda l: l.metrics.green_space_area_sqm)
    
    def get_balanced_layout(self) -> Optional[Layout]:
        """Get most balanced layout (normalized multi-objective)"""
        if not self.layouts:
            return None
        # Simple balanced score: normalize and average objectives
        def balance_score(layout: Layout):
            return (
                layout.metrics.sellable_ratio * 0.4 +
                layout.metrics.green_space_ratio * 0.3 +
                (1 - layout.metrics.road_efficiency) * 0.3
            )
        return max(self.layouts, key=balance_score)


@dataclass
class ComplianceReport:
    """Regulatory compliance report"""
    layout_id: str
    is_compliant: bool
    violations: List[str] = field(default_factory=list)
    warnings: List[str] = field(default_factory=list)
    checks_passed: List[str] = field(default_factory=list)
    
    def add_violation(self, message: str):
        """Add compliance violation"""
        self.violations.append(message)
        self.is_compliant = False
    
    def add_warning(self, message: str):
        """Add warning (non-critical)"""
        self.warnings.append(message)
    
    def add_pass(self, check_name: str):
        """Add passed check"""
        self.checks_passed.append(check_name)