973 lines
22 KiB
Markdown
973 lines
22 KiB
Markdown
|
|
# مستند کامل اپ `location_data`
|
||
|
|
|
||
|
|
این سند، وضعیت فعلی اپ `location_data` را به صورت کامل توضیح میدهد:
|
||
|
|
|
||
|
|
- مدلهای داده
|
||
|
|
- منطق business
|
||
|
|
- جریان ساخت location و block
|
||
|
|
- subdivision و خوشهبندی
|
||
|
|
- تولید analysis grid
|
||
|
|
- سنجشازدور با openEO
|
||
|
|
- تسکهای Celery
|
||
|
|
- APIهای فعلی
|
||
|
|
- ساختار responseها
|
||
|
|
- محدودیتها و فرضیات فعلی
|
||
|
|
|
||
|
|
این فایل بر اساس کد فعلی پروژه نوشته شده است و هدفش این است که یک مرجع فنی برای توسعهدهندههای بعدی باشد.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 1) هدف اپ `location_data`
|
||
|
|
|
||
|
|
اپ `location_data` در وضعیت فعلی چند نقش اصلی دارد:
|
||
|
|
|
||
|
|
1. نگهداری موقعیت جغرافیایی زمین با `lat/lon`
|
||
|
|
2. نگهداری مرز زمین یا مرز blockها
|
||
|
|
3. ساخت ساختار blockهای مزرعه
|
||
|
|
4. اجرای subdivision برای blockها
|
||
|
|
5. تولید grid analysis با ابعاد 30x30 متر
|
||
|
|
6. نگهداری نتایج سنجشازدور روی هر grid cell
|
||
|
|
7. نگهداری دادههای خاک و NDVI سنتی
|
||
|
|
8. فراهم کردن API برای:
|
||
|
|
- location data
|
||
|
|
- subdivision
|
||
|
|
- remote sensing trigger/result
|
||
|
|
- NDVI health
|
||
|
|
|
||
|
|
به صورت خلاصه، `location_data` الان فقط یک جدول مختصات نیست؛ بلکه هاب مکانی پروژه است.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 2) مفاهیم اصلی دامنه
|
||
|
|
|
||
|
|
### 2.1) SoilLocation
|
||
|
|
|
||
|
|
`SoilLocation` نماینده یک location اصلی برای یک مزرعه یا مرکز زمین است.
|
||
|
|
|
||
|
|
این مدل:
|
||
|
|
- مختصات `latitude` و `longitude` را نگه میدارد
|
||
|
|
- `farm_boundary` را ذخیره میکند
|
||
|
|
- تعداد blockهای اولیه را نگه میدارد
|
||
|
|
- `block_layout` را نگه میدارد
|
||
|
|
- مبنای ارتباط با:
|
||
|
|
- `SoilDepthData`
|
||
|
|
- `BlockSubdivision`
|
||
|
|
- `AnalysisGridCell`
|
||
|
|
- `RemoteSensingRun`
|
||
|
|
- `NdviObservation`
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### 2.2) BlockSubdivision
|
||
|
|
|
||
|
|
`BlockSubdivision` نتیجه خردسازی یک block است.
|
||
|
|
|
||
|
|
این مدل نگه میدارد:
|
||
|
|
- block code
|
||
|
|
- مرز همان block
|
||
|
|
- chunk size برای subdivision
|
||
|
|
- grid points اولیه
|
||
|
|
- centroid points نهایی
|
||
|
|
- elbow plot
|
||
|
|
- metadata الگوریتم
|
||
|
|
|
||
|
|
این مدل برای مرحلهای است که یک block را به بخشهای کوچکتر تقسیم میکنیم.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### 2.3) AnalysisGridCell
|
||
|
|
|
||
|
|
`AnalysisGridCell` سلولهای 30x30 متری تحلیل سنجشازدور را نگه میدارد.
|
||
|
|
|
||
|
|
هر cell:
|
||
|
|
- به یک `SoilLocation` وصل است
|
||
|
|
- در صورت نیاز به یک `BlockSubdivision` وصل است
|
||
|
|
- یک `cell_code` یکتا دارد
|
||
|
|
- geometry خودش را به صورت Polygon نگه میدارد
|
||
|
|
- centroid خودش را نگه میدارد
|
||
|
|
|
||
|
|
این مدل واحد اصلی تحلیل remote sensing است.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### 2.4) AnalysisGridObservation
|
||
|
|
|
||
|
|
`AnalysisGridObservation` داده زمانی هر سلول را نگه میدارد.
|
||
|
|
|
||
|
|
برای هر cell و بازه زمانی:
|
||
|
|
- `ndvi`
|
||
|
|
- `ndwi`
|
||
|
|
- `lst_c`
|
||
|
|
- `soil_vv`
|
||
|
|
- `soil_vv_db`
|
||
|
|
- `dem_m`
|
||
|
|
- `slope_deg`
|
||
|
|
|
||
|
|
ذخیره میشود.
|
||
|
|
|
||
|
|
این مدل cache دیتابیسی اصلی برای نتایج openEO است.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### 2.5) RemoteSensingRun
|
||
|
|
|
||
|
|
`RemoteSensingRun` وضعیت یک اجرای async سنجشازدور را نگه میدارد.
|
||
|
|
|
||
|
|
این مدل:
|
||
|
|
- به `SoilLocation` وصل است
|
||
|
|
- optionally به `BlockSubdivision` وصل است
|
||
|
|
- `block_code` و بازه زمانی را نگه میدارد
|
||
|
|
- status execution را نگه میدارد
|
||
|
|
- metadata مربوط به task/backend/result summary را نگه میدارد
|
||
|
|
|
||
|
|
این مدل برای tracking jobها در Celery استفاده میشود.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### 2.6) SoilDepthData
|
||
|
|
|
||
|
|
این مدل دادههای خاک را در عمقهای مختلف نگه میدارد:
|
||
|
|
- `0-5cm`
|
||
|
|
- `5-15cm`
|
||
|
|
- `15-30cm`
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### 2.7) NdviObservation
|
||
|
|
|
||
|
|
این مدل نگهدارنده NDVI سنتی است که جدا از workflow جدید openEO هم هنوز وجود دارد.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 3) ساختار فایلهای مهم اپ
|
||
|
|
|
||
|
|
```text
|
||
|
|
location_data/
|
||
|
|
├── admin.py
|
||
|
|
├── apps.py
|
||
|
|
├── block_subdivision.py
|
||
|
|
├── grid_analysis.py
|
||
|
|
├── models.py
|
||
|
|
├── ndvi.py
|
||
|
|
├── openeo_service.py
|
||
|
|
├── remote_sensing.py
|
||
|
|
├── serializers.py
|
||
|
|
├── soil_adapters.py
|
||
|
|
├── tasks.py
|
||
|
|
├── urls.py
|
||
|
|
├── views.py
|
||
|
|
├── migrations/
|
||
|
|
└── tests...
|
||
|
|
```
|
||
|
|
|
||
|
|
### نقش فایلها
|
||
|
|
|
||
|
|
- `models.py`: مدلهای اصلی
|
||
|
|
- `serializers.py`: serializerهای API
|
||
|
|
- `views.py`: endpointهای DRF
|
||
|
|
- `urls.py`: routeها
|
||
|
|
- `tasks.py`: تسکهای Celery
|
||
|
|
- `block_subdivision.py`: subdivision و elbow/kmeans
|
||
|
|
- `grid_analysis.py`: ساخت analysis grid cells
|
||
|
|
- `openeo_service.py`: لایه سرویس openEO
|
||
|
|
- `remote_sensing.py`: منطق قدیمیتر سنجشازدور/NDVI ساده
|
||
|
|
- `soil_adapters.py`: adapterهای داده خاک
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 4) تنظیمات مهم
|
||
|
|
|
||
|
|
### `SUBDIVISION_CHUNK_SQM`
|
||
|
|
|
||
|
|
در `config/settings.py`:
|
||
|
|
|
||
|
|
```python
|
||
|
|
SUBDIVISION_CHUNK_SQM = int(os.environ.get("SUBDIVISION_CHUNK_SQM", "900"))
|
||
|
|
```
|
||
|
|
|
||
|
|
مقدار پیشفرض فعلی:
|
||
|
|
- `900`
|
||
|
|
|
||
|
|
معنا:
|
||
|
|
- grid analysis با سلولهای `30m x 30m`
|
||
|
|
|
||
|
|
چون:
|
||
|
|
|
||
|
|
```text
|
||
|
|
step_m = sqrt(900) = 30m
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 5) مدلهای دیتابیس و منطق آنها
|
||
|
|
|
||
|
|
## 5.1) SoilLocation
|
||
|
|
|
||
|
|
فیلدهای مهم:
|
||
|
|
|
||
|
|
- `latitude`
|
||
|
|
- `longitude`
|
||
|
|
- `task_id`
|
||
|
|
- `farm_boundary`
|
||
|
|
- `input_block_count`
|
||
|
|
- `block_layout`
|
||
|
|
- `created_at`
|
||
|
|
- `updated_at`
|
||
|
|
|
||
|
|
### قید مهم
|
||
|
|
|
||
|
|
- `latitude + longitude` یکتا هستند
|
||
|
|
|
||
|
|
### block_layout
|
||
|
|
|
||
|
|
`block_layout` JSON summary کلی blockها را نگه میدارد.
|
||
|
|
|
||
|
|
نمونه:
|
||
|
|
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"input_block_count": 1,
|
||
|
|
"default_full_farm": true,
|
||
|
|
"algorithm_status": "completed",
|
||
|
|
"blocks": [
|
||
|
|
{
|
||
|
|
"block_code": "block-1",
|
||
|
|
"order": 1,
|
||
|
|
"source": "default",
|
||
|
|
"needs_subdivision": true,
|
||
|
|
"sub_blocks": [
|
||
|
|
{
|
||
|
|
"sub_block_code": "sub-block-1",
|
||
|
|
"centroid_lat": 35.689123,
|
||
|
|
"centroid_lon": 51.389456
|
||
|
|
}
|
||
|
|
],
|
||
|
|
"subdivision_summary": {
|
||
|
|
"chunk_size_sqm": 900,
|
||
|
|
"grid_point_count": 12,
|
||
|
|
"centroid_count": 3,
|
||
|
|
"optimal_k": 3
|
||
|
|
},
|
||
|
|
"analysis_grid_summary": {
|
||
|
|
"chunk_size_sqm": 900,
|
||
|
|
"cell_count": 18
|
||
|
|
}
|
||
|
|
}
|
||
|
|
]
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
`block_layout` canonical source نیست؛ بیشتر یک summary سریع برای API است.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 5.2) BlockSubdivision
|
||
|
|
|
||
|
|
فیلدهای مهم:
|
||
|
|
|
||
|
|
- `soil_location`
|
||
|
|
- `block_code`
|
||
|
|
- `source_boundary`
|
||
|
|
- `chunk_size_sqm`
|
||
|
|
- `grid_points`
|
||
|
|
- `centroid_points`
|
||
|
|
- `grid_point_count`
|
||
|
|
- `centroid_count`
|
||
|
|
- `elbow_plot`
|
||
|
|
- `status`
|
||
|
|
- `metadata`
|
||
|
|
|
||
|
|
### نقش
|
||
|
|
|
||
|
|
برای هر `block_code` در هر location، نتیجه subdivision در این مدل ذخیره میشود.
|
||
|
|
|
||
|
|
### metadata
|
||
|
|
|
||
|
|
شامل مواردی مثل:
|
||
|
|
- `estimated_area_sqm`
|
||
|
|
- `optimal_k`
|
||
|
|
- `inertia_curve`
|
||
|
|
- `analysis_grid`
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 5.3) RemoteSensingRun
|
||
|
|
|
||
|
|
فیلدهای مهم:
|
||
|
|
|
||
|
|
- `soil_location`
|
||
|
|
- `block_subdivision`
|
||
|
|
- `block_code`
|
||
|
|
- `provider`
|
||
|
|
- `chunk_size_sqm`
|
||
|
|
- `temporal_start`
|
||
|
|
- `temporal_end`
|
||
|
|
- `status`
|
||
|
|
- `metadata`
|
||
|
|
- `error_message`
|
||
|
|
- `started_at`
|
||
|
|
- `finished_at`
|
||
|
|
|
||
|
|
### statusها
|
||
|
|
|
||
|
|
- `pending`
|
||
|
|
- `running`
|
||
|
|
- `success`
|
||
|
|
- `failure`
|
||
|
|
|
||
|
|
### نقش
|
||
|
|
|
||
|
|
این جدول وضعیت اجرای async را نگه میدارد.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 5.4) AnalysisGridCell
|
||
|
|
|
||
|
|
فیلدهای مهم:
|
||
|
|
|
||
|
|
- `soil_location`
|
||
|
|
- `block_subdivision`
|
||
|
|
- `block_code`
|
||
|
|
- `cell_code`
|
||
|
|
- `chunk_size_sqm`
|
||
|
|
- `geometry`
|
||
|
|
- `centroid_lat`
|
||
|
|
- `centroid_lon`
|
||
|
|
|
||
|
|
### نقش
|
||
|
|
|
||
|
|
واحد spatial اصلی برای تحلیل remote sensing است.
|
||
|
|
|
||
|
|
### idempotency
|
||
|
|
|
||
|
|
سطح سرویس با این شرط enforce میشود:
|
||
|
|
- اگر برای یک `SoilLocation + block_code + chunk_size_sqm` cellها قبلاً ساخته شده باشند، دوباره ساخته نمیشوند.
|
||
|
|
|
||
|
|
### geometry
|
||
|
|
|
||
|
|
به صورت GeoJSON-like polygon ذخیره میشود.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 5.5) AnalysisGridObservation
|
||
|
|
|
||
|
|
فیلدهای مهم:
|
||
|
|
|
||
|
|
- `cell`
|
||
|
|
- `run`
|
||
|
|
- `temporal_start`
|
||
|
|
- `temporal_end`
|
||
|
|
- `ndvi`
|
||
|
|
- `ndwi`
|
||
|
|
- `lst_c`
|
||
|
|
- `soil_vv`
|
||
|
|
- `soil_vv_db`
|
||
|
|
- `dem_m`
|
||
|
|
- `slope_deg`
|
||
|
|
- `metadata`
|
||
|
|
|
||
|
|
### uniqueness
|
||
|
|
|
||
|
|
برای جلوگیری از duplicate:
|
||
|
|
- روی `cell + temporal_start + temporal_end` constraint داریم.
|
||
|
|
|
||
|
|
این باعث میشود cache دیتابیسی پایدار باشد.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 5.6) SoilDepthData
|
||
|
|
|
||
|
|
این مدل دادههای خاک را در عمقهای مختلف نگه میدارد.
|
||
|
|
|
||
|
|
هنوز به صورت مستقیم برای هر analysis grid cell جداگانه استفاده نشده است.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 5.7) NdviObservation
|
||
|
|
|
||
|
|
این مدل legacy / parallel NDVI store است.
|
||
|
|
|
||
|
|
workflow جدید openEO جایگزین آن نشده، بلکه کنار آن وجود دارد.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 6) منطق subdivision در `block_subdivision.py`
|
||
|
|
|
||
|
|
این فایل مسئول خردسازی blockها است.
|
||
|
|
|
||
|
|
### کارهایی که انجام میدهد
|
||
|
|
|
||
|
|
- استخراج polygon از boundary
|
||
|
|
- تبدیل مختصات جغرافیایی به مختصات محلی متری
|
||
|
|
- تولید grid points اولیه
|
||
|
|
- اجرای KMeans برای `K=1..10`
|
||
|
|
- محاسبه SSE/Inertia
|
||
|
|
- پیدا کردن elbow point
|
||
|
|
- انتخاب centroidها
|
||
|
|
- رسم elbow plot با matplotlib
|
||
|
|
- ذخیره plot در `ImageField`
|
||
|
|
- sync کردن نتیجه با `block_layout`
|
||
|
|
|
||
|
|
### input
|
||
|
|
|
||
|
|
ممکن است boundary به شکلهای زیر برسد:
|
||
|
|
- GeoJSON Polygon
|
||
|
|
- corners
|
||
|
|
- list مستقیم از points
|
||
|
|
|
||
|
|
### خروجی
|
||
|
|
|
||
|
|
- centroidهای نهایی block
|
||
|
|
- metadata الگوریتم
|
||
|
|
- elbow plot
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 7) منطق ساخت analysis grid در `grid_analysis.py`
|
||
|
|
|
||
|
|
این فایل مسئول تولید سلولهای 30x30 متری برای تحلیل remote sensing است.
|
||
|
|
|
||
|
|
### تابع اصلی
|
||
|
|
|
||
|
|
- `create_or_get_analysis_grid_cells(...)`
|
||
|
|
|
||
|
|
### ورودیها
|
||
|
|
|
||
|
|
- `location`
|
||
|
|
- optional `boundary`
|
||
|
|
- optional `block_code`
|
||
|
|
- optional `block_subdivision`
|
||
|
|
- optional `chunk_size_sqm`
|
||
|
|
|
||
|
|
### رفتار
|
||
|
|
|
||
|
|
1. chunk size را تعیین میکند
|
||
|
|
2. boundary را resolve میکند
|
||
|
|
3. polygon را extract میکند
|
||
|
|
4. اگر قبلاً برای همان `location + block_code + chunk_size` cell ساخته شده باشد، خروجی existing برمیگرداند
|
||
|
|
5. اگر نه، grid cellها ساخته میشوند و `AnalysisGridCell` ذخیره میشود
|
||
|
|
|
||
|
|
### نحوه ساخت شبکه
|
||
|
|
|
||
|
|
- polygon به دستگاه محلی متری تبدیل میشود
|
||
|
|
- `step_m = sqrt(chunk_size_sqm)` محاسبه میشود
|
||
|
|
- یک grid مستطیلی روی bounding box ساخته میشود
|
||
|
|
- هر cell که با polygon intersect داشته باشد نگه داشته میشود
|
||
|
|
|
||
|
|
### cell_code
|
||
|
|
|
||
|
|
فرمت فعلی deterministic است:
|
||
|
|
|
||
|
|
```text
|
||
|
|
loc-{location_id}__block-{block_code}__chunk-{chunk_size_sqm}__rXXXXcYYYY
|
||
|
|
```
|
||
|
|
|
||
|
|
### metadata summary
|
||
|
|
|
||
|
|
پس از ساخت grid:
|
||
|
|
- روی `BlockSubdivision.metadata["analysis_grid"]`
|
||
|
|
- و روی `SoilLocation.block_layout`
|
||
|
|
|
||
|
|
summary ذخیره میشود.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 8) منطق openEO در `openeo_service.py`
|
||
|
|
|
||
|
|
این فایل لایه service اصلی برای تحلیل openEO است.
|
||
|
|
|
||
|
|
### backend
|
||
|
|
|
||
|
|
```text
|
||
|
|
https://openeofed.dataspace.copernicus.eu
|
||
|
|
```
|
||
|
|
|
||
|
|
### هدف
|
||
|
|
|
||
|
|
گرفتن batch metricها برای مجموعهای از `AnalysisGridCell`ها.
|
||
|
|
|
||
|
|
### جریان کلی
|
||
|
|
|
||
|
|
1. اتصال و auth به openEO
|
||
|
|
2. ساخت `FeatureCollection` از cellها
|
||
|
|
3. ساخت `spatial_extent`
|
||
|
|
4. اجرای یک job per metric روی همه cellها
|
||
|
|
5. parse کردن نتیجه aggregate_spatial
|
||
|
|
6. merge کردن metricها روی map keyed by `cell_code`
|
||
|
|
|
||
|
|
### metricهای فعلی
|
||
|
|
|
||
|
|
- `ndvi` از `SENTINEL2_L2A`
|
||
|
|
- `ndwi` از `SENTINEL2_L2A`
|
||
|
|
- `lst_c` از `SENTINEL3_SLSTR_L2_LST`
|
||
|
|
- `soil_vv` از `SENTINEL1_GRD`
|
||
|
|
- `soil_vv_db` در Python از `soil_vv`
|
||
|
|
- `dem_m` از `COPERNICUS_30`
|
||
|
|
- `slope_deg` از DEM اگر backend پشتیبانی کند
|
||
|
|
|
||
|
|
### cloud mask Sentinel-2
|
||
|
|
|
||
|
|
کلاسهای معتبر SCL:
|
||
|
|
- `4`
|
||
|
|
- `5`
|
||
|
|
- `6`
|
||
|
|
|
||
|
|
نکته مهم:
|
||
|
|
- از `isin()` استفاده نمیشود
|
||
|
|
- فقط logical comparison استفاده میشود
|
||
|
|
|
||
|
|
### aggregate_spatial
|
||
|
|
|
||
|
|
فقط از:
|
||
|
|
|
||
|
|
```python
|
||
|
|
aggregate_spatial(geometries=feature_collection, reducer="mean")
|
||
|
|
```
|
||
|
|
|
||
|
|
استفاده میشود.
|
||
|
|
|
||
|
|
### slope support
|
||
|
|
|
||
|
|
اگر backend `slope()` را پشتیبانی نکند:
|
||
|
|
- `slope_deg = null`
|
||
|
|
- و metadata میگوید `slope_supported=False`
|
||
|
|
|
||
|
|
### normalized output
|
||
|
|
|
||
|
|
خروجی نهایی به این شکل است:
|
||
|
|
|
||
|
|
```python
|
||
|
|
{
|
||
|
|
"results": {
|
||
|
|
"cell-1": {
|
||
|
|
"ndvi": ...,
|
||
|
|
"ndwi": ...,
|
||
|
|
"lst_c": ...,
|
||
|
|
"soil_vv": ...,
|
||
|
|
"soil_vv_db": ...,
|
||
|
|
"dem_m": ...,
|
||
|
|
"slope_deg": ...,
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"metadata": {
|
||
|
|
"backend": "openeo",
|
||
|
|
"collections_used": [...],
|
||
|
|
"slope_supported": True,
|
||
|
|
"job_refs": {},
|
||
|
|
"failed_metrics": []
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 9) Celery workflow در `tasks.py`
|
||
|
|
|
||
|
|
### تسک قدیمی
|
||
|
|
|
||
|
|
- `fetch_soil_data_task`
|
||
|
|
|
||
|
|
برای خاک legacy است.
|
||
|
|
|
||
|
|
### workflow جدید remote sensing
|
||
|
|
|
||
|
|
تابع/تسکهای اصلی:
|
||
|
|
|
||
|
|
- `run_remote_sensing_analysis(...)`
|
||
|
|
- `run_remote_sensing_analysis_task.delay(...)`
|
||
|
|
|
||
|
|
### ورودی task
|
||
|
|
|
||
|
|
- `soil_location_id`
|
||
|
|
- optional `block_code`
|
||
|
|
- `temporal_start`
|
||
|
|
- `temporal_end`
|
||
|
|
- optional `force_refresh`
|
||
|
|
- optional `run_id`
|
||
|
|
|
||
|
|
### رفتار task
|
||
|
|
|
||
|
|
1. `SoilLocation` را پیدا میکند
|
||
|
|
2. `BlockSubdivision` را اگر لازم باشد resolve میکند
|
||
|
|
3. `RemoteSensingRun` را create/update میکند
|
||
|
|
4. `AnalysisGridCell`ها را ensure میکند
|
||
|
|
5. اگر observation برای همان range قبلاً باشد و `force_refresh=False`، دوباره process نمیکند
|
||
|
|
6. در غیر این صورت، `compute_remote_sensing_metrics()` را صدا میزند
|
||
|
|
7. `AnalysisGridObservation`ها را upsert میکند
|
||
|
|
8. status run را success/failure میکند
|
||
|
|
|
||
|
|
### idempotency
|
||
|
|
|
||
|
|
اگر observation قبلاً برای همان:
|
||
|
|
- cell
|
||
|
|
- temporal_start
|
||
|
|
- temporal_end
|
||
|
|
|
||
|
|
وجود داشته باشد، duplicate ساخته نمیشود.
|
||
|
|
|
||
|
|
### retry behavior
|
||
|
|
|
||
|
|
task روی خطاهای transient مثل:
|
||
|
|
- `OpenEOExecutionError`
|
||
|
|
- `OpenEOServiceError`
|
||
|
|
- request-level failures
|
||
|
|
|
||
|
|
retry میکند.
|
||
|
|
|
||
|
|
روی auth failure retry نمیکند.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 10) APIهای فعلی `location_data`
|
||
|
|
|
||
|
|
## 10.1) `GET /api/soil-data/`
|
||
|
|
|
||
|
|
کاربرد:
|
||
|
|
- فقط اطلاعات ذخیرهشده location را برمیگرداند
|
||
|
|
- subdivision را rerun نمیکند
|
||
|
|
|
||
|
|
ورودی:
|
||
|
|
- `lat`
|
||
|
|
- `lon`
|
||
|
|
- optional `block_code`
|
||
|
|
|
||
|
|
خروجی:
|
||
|
|
- location data
|
||
|
|
- block layout
|
||
|
|
- block subdivisions
|
||
|
|
- depths
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 10.2) `POST /api/soil-data/`
|
||
|
|
|
||
|
|
کاربرد:
|
||
|
|
- `SoilLocation` را create/get میکند
|
||
|
|
- در صورت نیاز `BlockSubdivision` میسازد
|
||
|
|
|
||
|
|
ورودی:
|
||
|
|
- `lat`
|
||
|
|
- `lon`
|
||
|
|
- `block_count`
|
||
|
|
- `block_code`
|
||
|
|
- `farm_boundary`
|
||
|
|
|
||
|
|
خروجی:
|
||
|
|
- location کامل
|
||
|
|
- `source` = `created` یا `database`
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 10.3) `GET /api/soil-data/tasks/<task_id>/status/`
|
||
|
|
|
||
|
|
کاربرد:
|
||
|
|
- status task قدیمی fetch خاک
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 10.4) `POST /api/soil-data/ndvi-health/`
|
||
|
|
|
||
|
|
کاربرد:
|
||
|
|
- NDVI health مستقل برای farm
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 10.5) `POST /api/soil-data/remote-sensing/`
|
||
|
|
|
||
|
|
کاربرد:
|
||
|
|
- remote sensing analysis را queue میکند
|
||
|
|
- heavy work را sync اجرا نمیکند
|
||
|
|
|
||
|
|
ورودی:
|
||
|
|
- `lat`
|
||
|
|
- `lon`
|
||
|
|
- optional `block_code`
|
||
|
|
- `start_date`
|
||
|
|
- `end_date`
|
||
|
|
- optional `force_refresh`
|
||
|
|
|
||
|
|
رفتار:
|
||
|
|
1. location را پیدا میکند
|
||
|
|
2. run میسازد
|
||
|
|
3. Celery task را enqueue میکند
|
||
|
|
4. `202 Accepted` برمیگرداند
|
||
|
|
|
||
|
|
خروجی شامل:
|
||
|
|
- `status=processing`
|
||
|
|
- `source=processing`
|
||
|
|
- `location`
|
||
|
|
- `block_code`
|
||
|
|
- `chunk_size_sqm`
|
||
|
|
- `temporal_extent`
|
||
|
|
- `summary` خالی
|
||
|
|
- `cells=[]`
|
||
|
|
- `run`
|
||
|
|
- `task_id`
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 10.6) `GET /api/soil-data/remote-sensing/`
|
||
|
|
|
||
|
|
کاربرد:
|
||
|
|
- فقط cache دیتابیسی remote sensing را میخواند
|
||
|
|
- هیچ openEO یا subdivision sync اجرا نمیکند
|
||
|
|
|
||
|
|
ورودی:
|
||
|
|
- `lat`
|
||
|
|
- `lon`
|
||
|
|
- optional `block_code`
|
||
|
|
- `start_date`
|
||
|
|
- `end_date`
|
||
|
|
|
||
|
|
خروجی حالتها:
|
||
|
|
|
||
|
|
### حالت 1: result موجود است
|
||
|
|
- `status=success`
|
||
|
|
- `source=database`
|
||
|
|
- summary metrics
|
||
|
|
- cells list
|
||
|
|
- run info
|
||
|
|
|
||
|
|
### حالت 2: result هنوز نیست ولی job در حال اجراست
|
||
|
|
- `status=processing`
|
||
|
|
- `source=processing`
|
||
|
|
- summary خالی
|
||
|
|
- cells empty
|
||
|
|
- run info
|
||
|
|
|
||
|
|
### حالت 3: location نیست
|
||
|
|
- `404`
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 11) serializerهای مهم
|
||
|
|
|
||
|
|
### `SoilDataRequestSerializer`
|
||
|
|
برای endpoint اصلی location.
|
||
|
|
|
||
|
|
### `SoilLocationResponseSerializer`
|
||
|
|
برای بازگشت location + blocks + depths.
|
||
|
|
|
||
|
|
### `BlockSubdivisionSerializer`
|
||
|
|
برای بازگشت subdivision data.
|
||
|
|
|
||
|
|
### `RemoteSensingTriggerSerializer`
|
||
|
|
برای trigger API remote sensing.
|
||
|
|
|
||
|
|
### `RemoteSensingCellObservationSerializer`
|
||
|
|
برای بازگشت per-cell remote sensing metrics.
|
||
|
|
|
||
|
|
### `RemoteSensingSummarySerializer`
|
||
|
|
برای بازگشت summary statisticها.
|
||
|
|
|
||
|
|
### `RemoteSensingRunSerializer`
|
||
|
|
برای بازگشت status run.
|
||
|
|
|
||
|
|
### `RemoteSensingResponseSerializer`
|
||
|
|
برای payload کامل remote sensing GET.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 12) منطق summary statistics در remote sensing GET
|
||
|
|
|
||
|
|
در response مربوط به `GET /remote-sensing/` این فیلدها برمیگردند:
|
||
|
|
|
||
|
|
- `cell_count`
|
||
|
|
- `ndvi_mean`
|
||
|
|
- `ndwi_mean`
|
||
|
|
- `lst_c_mean`
|
||
|
|
- `soil_vv_db_mean`
|
||
|
|
- `dem_m_mean`
|
||
|
|
- `slope_deg_mean`
|
||
|
|
|
||
|
|
اینها از روی observationهای موجود در DB محاسبه میشوند، نه از openEO live.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 13) admin
|
||
|
|
|
||
|
|
در `admin.py` الان موارد زیر رجیستر شدهاند:
|
||
|
|
|
||
|
|
- `SoilLocation`
|
||
|
|
- `SoilDepthData`
|
||
|
|
- `BlockSubdivision`
|
||
|
|
- `RemoteSensingRun`
|
||
|
|
- `AnalysisGridCell`
|
||
|
|
- `AnalysisGridObservation`
|
||
|
|
|
||
|
|
این باعث میشود debugging و inspection از طریق admin ممکن باشد.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 14) تستهای فعلی
|
||
|
|
|
||
|
|
### `test_soil_api.py`
|
||
|
|
- ساخت location
|
||
|
|
- ساخت subdivision
|
||
|
|
- رفتار GET/POST location
|
||
|
|
|
||
|
|
### `test_block_subdivision.py`
|
||
|
|
- elbow detection
|
||
|
|
- payload subdivision
|
||
|
|
|
||
|
|
### `test_grid_analysis.py`
|
||
|
|
- ساخت analysis grid 30x30
|
||
|
|
- idempotency grid cells
|
||
|
|
- استفاده از boundary location
|
||
|
|
|
||
|
|
### `test_openeo_service.py`
|
||
|
|
- parse نتیجه aggregate_spatial
|
||
|
|
- merge metricها
|
||
|
|
- conversion به dB
|
||
|
|
|
||
|
|
### `test_remote_sensing_api.py`
|
||
|
|
- queue شدن remote sensing task
|
||
|
|
- processing response
|
||
|
|
- cache read response
|
||
|
|
- not found behavior
|
||
|
|
|
||
|
|
### `test_ndvi_health_api.py`
|
||
|
|
- NDVI health API
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 15) وابستگیهای مهم
|
||
|
|
|
||
|
|
در `requirements.txt` dependencyهای مهم این بخشها شامل اینها هستند:
|
||
|
|
|
||
|
|
- `scikit-learn`
|
||
|
|
- `matplotlib`
|
||
|
|
- `Pillow`
|
||
|
|
- `numpy`
|
||
|
|
- `openeo`
|
||
|
|
|
||
|
|
### نقش آنها
|
||
|
|
|
||
|
|
- `scikit-learn`: KMeans
|
||
|
|
- `matplotlib`: elbow plot
|
||
|
|
- `Pillow`: ImageField support
|
||
|
|
- `numpy`: وابستگی عددی
|
||
|
|
- `openeo`: ارتباط با backend سنجشازدور
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 16) migrationهای مهم
|
||
|
|
|
||
|
|
- `0008_soillocation_block_layout.py`
|
||
|
|
- `0009_blocksubdivision.py`
|
||
|
|
- `0010_blocksubdivision_elbow_plot.py`
|
||
|
|
- `0011_remote_sensing_models.py`
|
||
|
|
|
||
|
|
این migrationها ساختار فعلی location/subdivision/remote sensing را ساختهاند.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 17) محدودیتها و فرضیات فعلی
|
||
|
|
|
||
|
|
### محدودیتهای فعلی
|
||
|
|
|
||
|
|
1. `block_layout` canonical source نیست و summary است.
|
||
|
|
2. subdivision و analysis grid دو لایه جدا هستند.
|
||
|
|
3. slope ممکن است روی backend همیشه پشتیبانی نشود.
|
||
|
|
4. API GET remote-sensing فقط cache میخواند.
|
||
|
|
5. هنوز endpoint مجزای status run نداریم.
|
||
|
|
6. grid generation از projection محلی استفاده میکند، نه GIS stack سنگین.
|
||
|
|
7. openEO calls فعلاً برای batch metric processing طراحی شدهاند، نه orchestration پیچیده job lifecycle.
|
||
|
|
|
||
|
|
### فرضیات
|
||
|
|
|
||
|
|
1. مزرعهها آنقدر کوچک هستند که local projected approximation مناسب باشد.
|
||
|
|
2. `SUBDIVISION_CHUNK_SQM=900` برای workflow فعلی درست است.
|
||
|
|
3. `cell_code` deterministic بودن برای idempotency کافی است.
|
||
|
|
4. `AnalysisGridObservation` cache اصلی remote sensing است.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 18) جریان کامل داده از ابتدا تا نتیجه remote sensing
|
||
|
|
|
||
|
|
### مرحله 1: ایجاد location
|
||
|
|
|
||
|
|
کاربر `POST /api/soil-data/` را صدا میزند.
|
||
|
|
|
||
|
|
نتیجه:
|
||
|
|
- `SoilLocation` ساخته میشود
|
||
|
|
- `farm_boundary` ذخیره میشود
|
||
|
|
- block layout ساخته میشود
|
||
|
|
- در صورت نیاز `BlockSubdivision` ساخته میشود
|
||
|
|
|
||
|
|
### مرحله 2: تولید analysis grid
|
||
|
|
|
||
|
|
وقتی task remote sensing اجرا میشود:
|
||
|
|
- اگر cellها قبلاً نباشند، `AnalysisGridCell`ها ساخته میشوند
|
||
|
|
|
||
|
|
### مرحله 3: اجرای openEO
|
||
|
|
|
||
|
|
Celery task:
|
||
|
|
- FeatureCollection از cellها میسازد
|
||
|
|
- metricها را batch اجرا میکند
|
||
|
|
- نتیجه را parse میکند
|
||
|
|
|
||
|
|
### مرحله 4: ذخیره observation
|
||
|
|
|
||
|
|
برای هر cell:
|
||
|
|
- یک `AnalysisGridObservation` برای بازه زمانی موردنظر ذخیره/آپدیت میشود
|
||
|
|
|
||
|
|
### مرحله 5: بازگشت نتیجه از API
|
||
|
|
|
||
|
|
کاربر `GET /api/soil-data/remote-sensing/` را صدا میزند.
|
||
|
|
|
||
|
|
سیستم:
|
||
|
|
- فقط DB را میخواند
|
||
|
|
- summary میسازد
|
||
|
|
- cells را برمیگرداند
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 19) پیشنهاد توسعه بعدی
|
||
|
|
|
||
|
|
برای ادامه توسعه، اینها منطقیترین قدمها هستند:
|
||
|
|
|
||
|
|
1. ساخت endpoint مستقل status برای `RemoteSensingRun`
|
||
|
|
2. اضافه کردن pagination برای cell responseها
|
||
|
|
3. اضافه کردن job reference واقعی openEO در metadata
|
||
|
|
4. پشتیبانی از چند resolution دیگر غیر از 30x30
|
||
|
|
5. ساخت serializer/model جدا برای summaryهای precomputed
|
||
|
|
6. اضافه کردن نمودارها یا aggregationهای block-level
|
||
|
|
7. اتصال remote sensing resultها به recommendation engine
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 20) جمعبندی نهایی
|
||
|
|
|
||
|
|
اپ `location_data` الان یک سیستم چندلایه است:
|
||
|
|
|
||
|
|
### لایه مکانی پایه
|
||
|
|
- `SoilLocation`
|
||
|
|
- `farm_boundary`
|
||
|
|
- `block_layout`
|
||
|
|
|
||
|
|
### لایه subdivision
|
||
|
|
- `BlockSubdivision`
|
||
|
|
- KMeans
|
||
|
|
- elbow plot
|
||
|
|
|
||
|
|
### لایه grid analysis
|
||
|
|
- `AnalysisGridCell`
|
||
|
|
|
||
|
|
### لایه observation
|
||
|
|
- `AnalysisGridObservation`
|
||
|
|
- `RemoteSensingRun`
|
||
|
|
|
||
|
|
### لایه سرویس
|
||
|
|
- `block_subdivision.py`
|
||
|
|
- `grid_analysis.py`
|
||
|
|
- `openeo_service.py`
|
||
|
|
- `tasks.py`
|
||
|
|
|
||
|
|
### لایه API
|
||
|
|
- `SoilDataView`
|
||
|
|
- `RemoteSensingAnalysisView`
|
||
|
|
- `NdviHealthView`
|
||
|
|
|
||
|
|
در نتیجه، `location_data` الان از یک app ساده location عبور کرده و به یک زیرسیستم کامل spatial + remote sensing تبدیل شده است.
|