File size: 3,148 Bytes
0939b04
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
from pydantic import BaseModel
from typing import Optional


class TerrainInfo(BaseModel):
    elevation_m: Optional[float] = None
    slope_deg: Optional[float] = None
    aspect_deg: Optional[float] = None
    is_south_facing: Optional[bool] = None


class ParcelInfo(BaseModel):
    pnu: Optional[str] = None
    jimok: Optional[str] = None
    jimok_name: Optional[str] = None
    area_m2: Optional[float] = None
    address: Optional[str] = None


class LandValueInfo(BaseModel):
    official_price_per_m2: Optional[float] = None
    ownership_type: Optional[str] = None
    slope_grade: Optional[str] = None


class RegulatoryInfo(BaseModel):
    agri_promotion: bool = False
    agri_unfavorable: bool = False
    natural_conservation: bool = False
    wetland_protection: bool = False
    forest_protection: bool = False
    greenbelt: bool = False
    water_source: bool = False
    wildlife_protection: bool = False
    steep_slope_hazard: bool = False
    disaster_risk: bool = False

    @property
    def any_blocked(self) -> bool:
        return any([
            self.agri_promotion, self.natural_conservation,
            self.wetland_protection, self.forest_protection,
            self.greenbelt, self.water_source,
            self.steep_slope_hazard, self.disaster_risk,
        ])


class SubstationInfo(BaseModel):
    name: Optional[str] = None
    distance_km: Optional[float] = None
    voltage_kv: Optional[str] = None
    remaining_kw: Optional[str] = None


class ScoreResult(BaseModel):
    total: int
    grade: str
    passed_hard_filter: bool
    breakdown: dict[str, float]


class AnalysisResult(BaseModel):
    lat: float
    lon: float
    annual_ghi_kwh: Optional[float] = None
    terrain: TerrainInfo = TerrainInfo()
    parcel: ParcelInfo = ParcelInfo()
    land_value: LandValueInfo = LandValueInfo()
    regulatory: RegulatoryInfo = RegulatoryInfo()
    substation: SubstationInfo = SubstationInfo()
    score: Optional[ScoreResult] = None


class ScanTile(BaseModel):
    lat_min: float
    lat_max: float
    lon_min: float
    lon_max: float
    prob: float
    detected: bool


class ScanRequest(BaseModel):
    lat_min: float
    lat_max: float
    lon_min: float
    lon_max: float
    threshold: float = 0.12


class ScanDetection(BaseModel):
    lat: float
    lon: float
    prob: float


class SolarInstallation(BaseModel):
    lat: float             # ํด๋Ÿฌ์Šคํ„ฐ ์ค‘์‹ฌ ์œ„๋„
    lon: float             # ํด๋Ÿฌ์Šคํ„ฐ ์ค‘์‹ฌ ๊ฒฝ๋„
    tile_count: int        # ํฌํ•จ ํƒ€์ผ ์ˆ˜
    area_m2: float         # ์ถ”์ • ํŒจ๋„ ๋ฉด์  (mยฒ)
    capacity_kw: float     # ์ถ”์ • ์„ค์น˜ ์šฉ๋Ÿ‰ (kW)
    annual_kwh: float      # ์ถ”์ • ์—ฐ๊ฐ„ ๋ฐœ์ „๋Ÿ‰ (kWh)
    revenue_krw: int       # ์ถ”์ • ์—ฐ๊ฐ„ ๋งค์ถœ (์›)
    max_prob: float        # ํด๋Ÿฌ์Šคํ„ฐ ๋‚ด ์ตœ๋Œ€ ์‹ ๋ขฐ๋„
    address: Optional[str] = None


class ScanResult(BaseModel):
    detections: list[ScanDetection]
    installations: list[SolarInstallation] = []
    tiles: list[ScanTile] = []
    tiles_scanned: int
    tiles_positive: int
    total_area_m2: float = 0.0
    total_capacity_kw: float = 0.0
    total_revenue_krw: int = 0