UPDATE
This commit is contained in:
@@ -0,0 +1,371 @@
|
||||
# ساختار فعلی `location_data`
|
||||
|
||||
این فایل وضعیت فعلی اپ `location_data` را توضیح میدهد؛ هم از نظر ساختار فایلها و هم از نظر مدلها، APIها و جریان داده.
|
||||
|
||||
## هدف فعلی اپ
|
||||
|
||||
اپ `location_data` فعلاً این مسئولیتها را دارد:
|
||||
|
||||
- نگهداری موقعیت زمین با `lat` و `lon`
|
||||
- نگهداری مرز مزرعه در `farm_boundary`
|
||||
- نگهداری ساختار بلوکهای زمین در `block_layout`
|
||||
- نگهداری دادههای خاک برای عمقهای مختلف در `SoilDepthData`
|
||||
- نگهداری مشاهدات NDVI در `NdviObservation`
|
||||
- برگرداندن ساختار بلوکهای زمین از API محلی بدون نیاز به API خارجی در فاز فعلی
|
||||
|
||||
نکته مهم:
|
||||
|
||||
- در فاز فعلی، endpoint اصلی `location_data` برای ساختار زمین، فقط داده را در دیتابیس میخواند/ذخیره میکند.
|
||||
- فعلاً برای بلوکها، زیربلوکها و دادههای ماهوارهای هیچ درخواست خارجی زده نمیشود.
|
||||
|
||||
## ساختار فایلها
|
||||
|
||||
```text
|
||||
location_data/
|
||||
├── admin.py
|
||||
├── apps.py
|
||||
├── models.py
|
||||
├── serializers.py
|
||||
├── views.py
|
||||
├── urls.py
|
||||
├── tasks.py
|
||||
├── soil_adapters.py
|
||||
├── remote_sensing.py
|
||||
├── ndvi.py
|
||||
├── test_soil_api.py
|
||||
├── test_soil_adapters.py
|
||||
├── test_ndvi_health_api.py
|
||||
├── postman/
|
||||
│ └── soil_data.json
|
||||
├── management/
|
||||
│ └── commands/
|
||||
└── migrations/
|
||||
├── 0001_initial.py
|
||||
├── 0002_soildepthdata_refactor.py
|
||||
├── 0002_soillocation_ideal_sensor_profile.py
|
||||
├── 0003_rename_app_label.py
|
||||
├── 0004_soillocation_farm_boundary.py
|
||||
├── 0005_merge_20260327_0840.py
|
||||
├── 0006_remove_soillocation_ideal_sensor_profile.py
|
||||
├── 0007_ndviobservation.py
|
||||
└── 0008_soillocation_block_layout.py
|
||||
```
|
||||
|
||||
## مدلها و جدولها
|
||||
|
||||
### 1) `SoilLocation`
|
||||
|
||||
مدل اصلی اپ است و نماینده یک موقعیت یکتا برای زمین یا مرکز زمین محسوب میشود.
|
||||
|
||||
فیلدهای اصلی:
|
||||
|
||||
| فیلد | نوع | توضیح |
|
||||
|---|---|---|
|
||||
| `id` | `BigAutoField` | شناسه داخلی رکورد |
|
||||
| `latitude` | `DecimalField(9,6)` | عرض جغرافیایی |
|
||||
| `longitude` | `DecimalField(9,6)` | طول جغرافیایی |
|
||||
| `task_id` | `CharField` | شناسه تسک Celery برای جریان قدیمی واکشی خاک |
|
||||
| `farm_boundary` | `JSONField` | مرز مزرعه به شکل Polygon یا corners |
|
||||
| `input_block_count` | `PositiveIntegerField` | تعداد بلوک اولیهای که از ورودی کشاورز میآید |
|
||||
| `block_layout` | `JSONField` | ساختار بلوکها و زیربلوکهای زمین |
|
||||
| `created_at` | `DateTimeField` | زمان ایجاد |
|
||||
| `updated_at` | `DateTimeField` | زمان آخرین تغییر |
|
||||
|
||||
قیدها:
|
||||
|
||||
- روی ترکیب `latitude` و `longitude` یکتا است.
|
||||
|
||||
رفتار مهم:
|
||||
|
||||
- اگر `input_block_count` ارسال نشود، مقدار پیشفرض `1` است.
|
||||
- اگر `block_layout` خالی باشد، به صورت خودکار با یک بلوک کامل ساخته میشود.
|
||||
- متد `set_input_block_count()` ساختار اولیه بلوکها را میسازد.
|
||||
|
||||
### 2) `SoilDepthData`
|
||||
|
||||
این مدل دادههای خاک را برای هر عمق نگه میدارد و به `SoilLocation` وصل است.
|
||||
|
||||
عمقهای فعلی:
|
||||
|
||||
- `0-5cm`
|
||||
- `5-15cm`
|
||||
- `15-30cm`
|
||||
|
||||
فیلدهای مهم:
|
||||
|
||||
| فیلد | نوع | توضیح |
|
||||
|---|---|---|
|
||||
| `soil_location` | `ForeignKey` | ارتباط با `SoilLocation` |
|
||||
| `depth_label` | `CharField` | برچسب عمق |
|
||||
| `bdod` تا `wv1500` | `FloatField` | پارامترهای مختلف خاک |
|
||||
| `created_at` | `DateTimeField` | زمان ثبت رکورد |
|
||||
|
||||
قیدها:
|
||||
|
||||
- برای هر `soil_location` و هر `depth_label` فقط یک رکورد وجود دارد.
|
||||
|
||||
### 3) `NdviObservation`
|
||||
|
||||
این مدل برای ذخیره مشاهدههای NDVI استفاده میشود.
|
||||
|
||||
فیلدهای مهم:
|
||||
|
||||
| فیلد | نوع | توضیح |
|
||||
|---|---|---|
|
||||
| `location` | `ForeignKey` | ارتباط با `SoilLocation` |
|
||||
| `observation_date` | `DateField` | تاریخ مشاهده |
|
||||
| `mean_ndvi` | `FloatField` | میانگین NDVI |
|
||||
| `ndvi_map` | `JSONField` | داده مکانی NDVI |
|
||||
| `vegetation_health_class` | `CharField` | کلاس سلامت پوشش گیاهی |
|
||||
| `satellite_source` | `CharField` | منبع تصویر ماهوارهای |
|
||||
| `cloud_cover` | `FloatField` | درصد ابر |
|
||||
| `metadata` | `JSONField` | داده تکمیلی |
|
||||
|
||||
## ساختار `block_layout`
|
||||
|
||||
فیلد `block_layout` فعلاً ساختار پایه تقسیم زمین را نگه میدارد.
|
||||
|
||||
نمونه پیشفرض وقتی کل زمین یک بلوک باشد:
|
||||
|
||||
```json
|
||||
{
|
||||
"input_block_count": 1,
|
||||
"default_full_farm": true,
|
||||
"algorithm_status": "pending",
|
||||
"blocks": [
|
||||
{
|
||||
"block_code": "block-1",
|
||||
"order": 1,
|
||||
"source": "default",
|
||||
"needs_subdivision": null,
|
||||
"sub_blocks": []
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
نمونه وقتی ورودی مثلاً `block_count = 3` باشد:
|
||||
|
||||
```json
|
||||
{
|
||||
"input_block_count": 3,
|
||||
"default_full_farm": false,
|
||||
"algorithm_status": "pending",
|
||||
"blocks": [
|
||||
{
|
||||
"block_code": "block-1",
|
||||
"order": 1,
|
||||
"source": "input",
|
||||
"needs_subdivision": null,
|
||||
"sub_blocks": []
|
||||
},
|
||||
{
|
||||
"block_code": "block-2",
|
||||
"order": 2,
|
||||
"source": "input",
|
||||
"needs_subdivision": null,
|
||||
"sub_blocks": []
|
||||
},
|
||||
{
|
||||
"block_code": "block-3",
|
||||
"order": 3,
|
||||
"source": "input",
|
||||
"needs_subdivision": null,
|
||||
"sub_blocks": []
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
معنای فیلدها:
|
||||
|
||||
| فیلد | توضیح |
|
||||
|---|---|
|
||||
| `input_block_count` | تعداد بلوک اولیه |
|
||||
| `default_full_farm` | آیا کل زمین هنوز یک بلوک کامل است یا نه |
|
||||
| `algorithm_status` | وضعیت اجرای الگوریتم تقسیمبندی |
|
||||
| `blocks` | لیست بلوکهای فعلی |
|
||||
| `block_code` | کد بلوک |
|
||||
| `order` | ترتیب بلوک |
|
||||
| `source` | منشأ بلوک: `default` یا `input` |
|
||||
| `needs_subdivision` | آیا الگوریتم تشخیص داده که این بلوک باید خردتر شود یا نه |
|
||||
| `sub_blocks` | لیست زیربلوکها |
|
||||
|
||||
## Serializerها
|
||||
|
||||
### `SoilDataRequestSerializer`
|
||||
|
||||
ورودی endpoint اصلی `location_data`:
|
||||
|
||||
| فیلد | اجباری | توضیح |
|
||||
|---|---|---|
|
||||
| `lat` | بله | عرض جغرافیایی |
|
||||
| `lon` | بله | طول جغرافیایی |
|
||||
| `block_count` | خیر | تعداد بلوک اولیه، پیشفرض `1` |
|
||||
|
||||
### `SoilLocationResponseSerializer`
|
||||
|
||||
خروجی اصلی برای یک location:
|
||||
|
||||
- `id`
|
||||
- `lat`
|
||||
- `lon`
|
||||
- `input_block_count`
|
||||
- `block_layout`
|
||||
- `depths`
|
||||
|
||||
### `SoilDepthDataSerializer`
|
||||
|
||||
لیست پارامترهای خاک برای هر عمق را برمیگرداند.
|
||||
|
||||
### `NdviHealthRequestSerializer` و `NdviHealthResponseSerializer`
|
||||
|
||||
برای endpoint مربوط به NDVI استفاده میشوند.
|
||||
|
||||
## Viewها و APIها
|
||||
|
||||
### 1) `SoilDataView`
|
||||
|
||||
مسیر:
|
||||
|
||||
- `GET /api/soil-data/`
|
||||
- `POST /api/soil-data/`
|
||||
|
||||
وظیفه فعلی:
|
||||
|
||||
- گرفتن `lat` و `lon`
|
||||
- گرفتن `block_count` در صورت وجود
|
||||
- ساخت یا پیدا کردن `SoilLocation`
|
||||
- ذخیره `input_block_count`
|
||||
- ساخت `block_layout`
|
||||
- برگرداندن پاسخ با `source = local`
|
||||
|
||||
رفتار فعلی:
|
||||
|
||||
- اگر location وجود نداشته باشد، ساخته میشود.
|
||||
- اگر `block_count` تغییر کند، ساختار `block_layout` دوباره ساخته میشود.
|
||||
- فعلاً هیچ fetch خارجی برای اطلاعات خاک یا ماهوارهای انجام نمیشود.
|
||||
|
||||
### 2) `SoilDataTaskStatusView`
|
||||
|
||||
مسیر:
|
||||
|
||||
- `GET /api/soil-data/tasks/<task_id>/status/`
|
||||
|
||||
وضعیت فعلی:
|
||||
|
||||
- هنوز در کد وجود دارد.
|
||||
- برای جریان قدیمی مبتنی بر Celery طراحی شده است.
|
||||
- با تغییر اخیر، endpoint اصلی `location_data` دیگر بهطور پیشفرض task جدیدی صف نمیکند.
|
||||
|
||||
### 3) `NdviHealthView`
|
||||
|
||||
مسیر:
|
||||
|
||||
- `POST /api/soil-data/ndvi-health/`
|
||||
|
||||
وظیفه:
|
||||
|
||||
- دریافت `farm_uuid`
|
||||
- خواندن داده NDVI از سرویس داخلی NDVI
|
||||
- برگرداندن اطلاعات سلامت پوشش گیاهی
|
||||
|
||||
## فایل `tasks.py`
|
||||
|
||||
این فایل هنوز منطق قدیمی واکشی داده خاک را نگه میدارد.
|
||||
|
||||
اجزای اصلی:
|
||||
|
||||
- `fetch_soil_data_for_coordinates()`
|
||||
- `fetch_soil_data_task()`
|
||||
|
||||
نکته:
|
||||
|
||||
- این بخش هنوز برای سازگاری و جریانهای قدیمی در پروژه باقی مانده است.
|
||||
- ولی در فاز فعلی تقسیم بلوکها، از این task برای endpoint اصلی `location_data` استفاده نمیشود.
|
||||
|
||||
## فایل `soil_adapters.py`
|
||||
|
||||
این فایل abstraction مربوط به تامین داده خاک را نگه میدارد.
|
||||
|
||||
کاربرد آن:
|
||||
|
||||
- mock provider
|
||||
- live soil provider
|
||||
- ساختار depth-based data fetch
|
||||
|
||||
در وضعیت فعلی:
|
||||
|
||||
- برای منطق بلوکبندی فعلی لازم نیست.
|
||||
- اما برای جریان قدیمی یا مراحل بعدی میتواند دوباره استفاده شود.
|
||||
|
||||
## فایل `remote_sensing.py`
|
||||
|
||||
این فایل مربوط به منطق سنجشازدور و دادههای ماهوارهای است.
|
||||
|
||||
در وضعیت فعلی:
|
||||
|
||||
- برای block layout فعلاً استفاده فعال ندارد.
|
||||
- بعداً میتواند برای تحلیل هر بلوک یا زیربلوک استفاده شود.
|
||||
|
||||
## فایل `ndvi.py`
|
||||
|
||||
این فایل سرویس/منطق NDVI را نگه میدارد و برای endpoint NDVI استفاده میشود.
|
||||
|
||||
## migrationها
|
||||
|
||||
مهمترین migrationهای فعلی:
|
||||
|
||||
| migration | توضیح |
|
||||
|---|---|
|
||||
| `0001_initial.py` | ساختار اولیه `SoilLocation` |
|
||||
| `0002_soildepthdata_refactor.py` | جداسازی دادههای عمقی در `SoilDepthData` |
|
||||
| `0004_soillocation_farm_boundary.py` | اضافه شدن `farm_boundary` |
|
||||
| `0007_ndviobservation.py` | اضافه شدن `NdviObservation` |
|
||||
| `0008_soillocation_block_layout.py` | اضافه شدن `input_block_count` و `block_layout` |
|
||||
|
||||
## تستها
|
||||
|
||||
فایلهای تست اصلی:
|
||||
|
||||
- `location_data/test_soil_api.py`
|
||||
- تست ساختار محلی بلوکها
|
||||
- تست پیشفرض یک بلوک
|
||||
- تست تغییر `block_count`
|
||||
|
||||
- `location_data/test_soil_adapters.py`
|
||||
- تست adapterهای خاک
|
||||
- تست ذخیره depth data
|
||||
|
||||
- `location_data/test_ndvi_health_api.py`
|
||||
- تست endpoint NDVI
|
||||
|
||||
## ارتباط با `farm_data`
|
||||
|
||||
`location_data` مستقیماً توسط `farm_data` استفاده میشود.
|
||||
|
||||
نمونه وابستگیها:
|
||||
|
||||
- `farm_data` از `SoilLocation` به عنوان `center_location` استفاده میکند.
|
||||
- `farm_boundary` از سمت `farm_data` میآید.
|
||||
- `block_count` هم از ورودی `farm_data` قابل ثبت است.
|
||||
- `farm_data` فعلاً فقط location و block layout را ذخیره میکند و برای این بخش sync خارجی انجام نمیدهد.
|
||||
|
||||
## جمعبندی ساختار فعلی
|
||||
|
||||
الان `location_data` دو لایه دارد:
|
||||
|
||||
1. لایه فعلی فعال برای بلوکبندی زمین
|
||||
- محلی
|
||||
- ساده
|
||||
- بدون API خارجی
|
||||
- با `input_block_count` و `block_layout`
|
||||
|
||||
2. لایه قدیمی/جانبی برای خاک و NDVI
|
||||
- `SoilDepthData`
|
||||
- `tasks.py`
|
||||
- `soil_adapters.py`
|
||||
- `NdviObservation`
|
||||
- `remote_sensing.py`
|
||||
|
||||
یعنی از نظر معماری، اپ الان هم داده مکانی زمین را نگه میدارد و هم زیرساختی برای تحلیل خاک/NDVI دارد، ولی منطق جدید بلوکها فعلاً مستقل و محلی پیاده شده است.
|
||||
@@ -0,0 +1,685 @@
|
||||
# مستند کامل عملکرد فعلی `location_data`
|
||||
|
||||
این فایل شرح میدهد که اپ `location_data` در وضعیت فعلی دقیقاً چه کاری انجام میدهد، چه مدلهایی دارد، جریان درخواستها چگونه است، منطق تقسیمبندی بلوکها چگونه اجرا میشود و چه بخشهایی فقط داده ذخیرهشده را برمیگردانند.
|
||||
|
||||
---
|
||||
|
||||
## 1) هدف فعلی اپ `location_data`
|
||||
|
||||
اپ `location_data` در وضعیت فعلی چند مسئولیت اصلی دارد:
|
||||
|
||||
- نگهداری موقعیت جغرافیایی زمین با `lat` و `lon`
|
||||
- نگهداری مرز زمین یا بلوک در `farm_boundary`
|
||||
- نگهداری ساختار بلوکهای اصلی زمین در `block_layout`
|
||||
- نگهداری نتیجه خردسازی هوشمند هر بلوک در مدل `BlockSubdivision`
|
||||
- تولید نقاط شبکهای 100 متری یا هر اندازهای که با `SUBDIVISION_CHUNK_SQM` تنظیم شود
|
||||
- اجرای خوشهبندی `KMeans` روی نقاط شبکهای
|
||||
- پیدا کردن تعداد بهینه خوشهها با روش `Elbow`
|
||||
- ذخیره centroidهای نهایی هر بخش خردشده
|
||||
- تولید و ذخیره تصویر نمودار `K-SSE` برای هر subdivision
|
||||
- نگهداری دادههای خاک در `SoilDepthData`
|
||||
- نگهداری دادههای NDVI در `NdviObservation`
|
||||
|
||||
نکته مهم:
|
||||
|
||||
- در فاز فعلی، `GET` هیچ پردازش جدیدی انجام نمیدهد.
|
||||
- تمام پردازش subdivision فقط در زمان `POST` و فقط اگر subdivision آن بلوک قبلاً ساخته نشده باشد اجرا میشود.
|
||||
|
||||
---
|
||||
|
||||
## 2) تنظیمات محیطی
|
||||
|
||||
### `SUBDIVISION_CHUNK_SQM`
|
||||
|
||||
در `config/settings.py` یک متغیر جدید اضافه شده است:
|
||||
|
||||
- `SUBDIVISION_CHUNK_SQM`
|
||||
- مقدار پیشفرض: `100`
|
||||
- واحد: متر مربع
|
||||
|
||||
کاربرد:
|
||||
|
||||
- تعیین میکند شبکه اولیه برای subdivision با چه اندازهای ساخته شود.
|
||||
- اگر مقدار `100` باشد، هر chunk تقریباً یک سلول `10m x 10m` خواهد بود، چون:
|
||||
|
||||
```text
|
||||
step = sqrt(100) = 10 meters
|
||||
```
|
||||
|
||||
این مقدار از `.env` یا environment خوانده میشود:
|
||||
|
||||
```env
|
||||
SUBDIVISION_CHUNK_SQM=100
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3) مدلهای اصلی اپ
|
||||
|
||||
## 3.1) `SoilLocation`
|
||||
|
||||
این مدل رکورد اصلی location را نگه میدارد.
|
||||
|
||||
### فیلدها
|
||||
|
||||
- `latitude`
|
||||
- `longitude`
|
||||
- `task_id`
|
||||
- `farm_boundary`
|
||||
- `input_block_count`
|
||||
- `block_layout`
|
||||
- `created_at`
|
||||
- `updated_at`
|
||||
|
||||
### نقش
|
||||
|
||||
- هر location با ترکیب `latitude + longitude` یکتا است.
|
||||
- اطلاعات کلی زمین یا مرکز زمین را نگه میدارد.
|
||||
- اگر هنوز هیچ تقسیمبندی انجام نشده باشد، ساختار اولیه بلوکها را در `block_layout` نگه میدارد.
|
||||
|
||||
### `block_layout`
|
||||
|
||||
این فیلد JSON ساختار بلوکها را نگه میدارد. نمونه ساده:
|
||||
|
||||
```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": 100,
|
||||
"grid_point_count": 24,
|
||||
"centroid_count": 3,
|
||||
"optimal_k": 3
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### رفتار مهم
|
||||
|
||||
- اگر `block_layout` خالی باشد، به صورت پیشفرض با یک بلوک کامل ساخته میشود.
|
||||
- متد `set_input_block_count()` ساختار اولیه بلوکهای اصلی را میسازد.
|
||||
|
||||
---
|
||||
|
||||
## 3.2) `BlockSubdivision`
|
||||
|
||||
این مدل نتیجه واقعی subdivision برای هر بلوک را ذخیره میکند.
|
||||
|
||||
### فیلدها
|
||||
|
||||
- `soil_location`: ارتباط با `SoilLocation`
|
||||
- `block_code`: شناسه بلوکی که subdivision روی آن اجرا شده
|
||||
- `source_boundary`: مرز همان بلوک
|
||||
- `chunk_size_sqm`: اندازه هر chunk
|
||||
- `grid_points`: نقاط اولیه شبکه
|
||||
- `centroid_points`: centroidهای نهایی خوشهها
|
||||
- `grid_point_count`: تعداد نقاط اولیه
|
||||
- `centroid_count`: تعداد centroidهای نهایی
|
||||
- `elbow_plot`: تصویر نمودار elbow
|
||||
- `status`: وضعیت رکورد
|
||||
- `metadata`: داده تکمیلی مانند `optimal_k` و `inertia_curve`
|
||||
- `created_at`
|
||||
- `updated_at`
|
||||
|
||||
### نقش
|
||||
|
||||
این مدل منبع اصلی داده subdivision است.
|
||||
|
||||
یعنی:
|
||||
|
||||
- نقاط خام شبکه در این مدل ذخیره میشوند
|
||||
- centroidهای نهایی هم در این مدل ذخیره میشوند
|
||||
- نمودار elbow هم در همین مدل ذخیره میشود
|
||||
|
||||
### قید یکتا
|
||||
|
||||
برای هر location و هر `block_code` فقط یک subdivision وجود دارد:
|
||||
|
||||
```text
|
||||
(soil_location, block_code) unique
|
||||
```
|
||||
|
||||
بنابراین اگر برای یک بلوک قبلاً subdivision ساخته شده باشد، دوباره ایجاد نمیشود.
|
||||
|
||||
---
|
||||
|
||||
## 3.3) `SoilDepthData`
|
||||
|
||||
این مدل دادههای خاک برای عمقهای مختلف را نگه میدارد.
|
||||
|
||||
عمقهای فعلی:
|
||||
|
||||
- `0-5cm`
|
||||
- `5-15cm`
|
||||
- `15-30cm`
|
||||
|
||||
این بخش در حال حاضر مستقل از subdivision است و هنوز برای هر sub-block جداگانه داده خاک تولید نمیکند.
|
||||
|
||||
---
|
||||
|
||||
## 3.4) `NdviObservation`
|
||||
|
||||
این مدل دادههای NDVI و سلامت پوشش گیاهی را نگه میدارد.
|
||||
|
||||
این بخش هم فعلاً مستقل از منطق subdivision است.
|
||||
|
||||
---
|
||||
|
||||
## 4) فایل `block_subdivision.py`
|
||||
|
||||
فایل `location_data/block_subdivision.py` مرکز اصلی منطق هوشمند subdivision است.
|
||||
|
||||
### وظایف اصلی این فایل
|
||||
|
||||
- استخراج polygon از ورودی
|
||||
- تبدیل مختصات جغرافیایی به صفحه محلی متری
|
||||
- ساخت grid points با اندازه chunk مشخص
|
||||
- اجرای `KMeans` برای `K=1..10`
|
||||
- ذخیره `SSE` یا همان `Inertia`
|
||||
- پیدا کردن elbow point
|
||||
- ساخت centroidهای نهایی خوشهها
|
||||
- sync کردن نتیجه با `block_layout`
|
||||
- تولید تصویر نمودار elbow
|
||||
- ذخیره تصویر در مدل با `ContentFile`
|
||||
|
||||
---
|
||||
|
||||
## 5) روند هندسی subdivision
|
||||
|
||||
## 5.1) استخراج Polygon
|
||||
|
||||
ورودی boundary میتواند به چند شکل بیاید:
|
||||
|
||||
- GeoJSON Polygon
|
||||
- `corners`
|
||||
- آرایه مستقیم از نقاط
|
||||
|
||||
تابع `extract_polygon()` این ورودی را به لیستی از نقاط جغرافیایی تبدیل میکند.
|
||||
|
||||
نمونه ورودی معتبر:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "Polygon",
|
||||
"coordinates": [
|
||||
[
|
||||
[51.3890, 35.6890],
|
||||
[51.3902, 35.6890],
|
||||
[51.3902, 35.6900],
|
||||
[51.3890, 35.6900],
|
||||
[51.3890, 35.6890]
|
||||
]
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5.2) تبدیل مختصات به فضای محلی متری
|
||||
|
||||
برای اینکه بتوانیم فاصلهها و گریدبندی را بر اساس متر حساب کنیم، polygon از مختصات جغرافیایی به مختصات محلی متری تبدیل میشود.
|
||||
|
||||
تابع مربوط:
|
||||
|
||||
- `project_polygon_to_local_meters()`
|
||||
|
||||
ویژگی این تبدیل:
|
||||
|
||||
- نقطه اول polygon به عنوان origin در نظر گرفته میشود
|
||||
- با تقریب محلی، `lat/lon` به `x/y` در واحد متر تبدیل میشوند
|
||||
|
||||
این تبدیل برای subdivision کوچک و محلی مناسب است.
|
||||
|
||||
---
|
||||
|
||||
## 5.3) تولید grid points
|
||||
|
||||
تابع:
|
||||
|
||||
- `generate_grid_points()`
|
||||
|
||||
منطق:
|
||||
|
||||
1. ابتدا اندازه گام محاسبه میشود:
|
||||
|
||||
```text
|
||||
step_m = sqrt(chunk_size_sqm)
|
||||
```
|
||||
|
||||
2. روی bounding box polygon، نقاط مرکزی grid بررسی میشوند.
|
||||
3. هر نقطهای که داخل polygon باشد نگه داشته میشود.
|
||||
|
||||
خروجی:
|
||||
|
||||
- `grid_points`: مختصات جغرافیایی قابل ذخیره در JSON
|
||||
- `grid_vectors`: مختصات محلی متری برای ورود به `KMeans`
|
||||
|
||||
نمونه هر grid point:
|
||||
|
||||
```json
|
||||
{
|
||||
"point_code": "pt-1",
|
||||
"lat": 35.689123,
|
||||
"lon": 51.389456
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6) الگوریتم خوشهبندی هوشمند
|
||||
|
||||
## 6.1) اجرای `KMeans`
|
||||
|
||||
تابع:
|
||||
|
||||
- `cluster_grid_points()`
|
||||
|
||||
منطق:
|
||||
|
||||
- روی `grid_vectors` خوشهبندی انجام میشود
|
||||
- برای `K=1` تا `K=10` اجرا میشود
|
||||
- اگر تعداد نقاط کمتر از 10 باشد، `max_k = len(grid_vectors)` در نظر گرفته میشود
|
||||
|
||||
برای هر `K`:
|
||||
|
||||
- مدل `KMeans` ساخته میشود
|
||||
- `fit()` اجرا میشود
|
||||
- مقدار `model.inertia_` به عنوان `SSE` ذخیره میشود
|
||||
|
||||
خروجی میانی:
|
||||
|
||||
```json
|
||||
[
|
||||
{"k": 1, "sse": 1300.5},
|
||||
{"k": 2, "sse": 640.2},
|
||||
{"k": 3, "sse": 390.1}
|
||||
]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6.2) پیدا کردن Elbow Point
|
||||
|
||||
تابع:
|
||||
|
||||
- `detect_elbow_point()`
|
||||
|
||||
منطق فعلی:
|
||||
|
||||
1. از روی SSEها، شیب افت بین نقاط متوالی محاسبه میشود.
|
||||
2. سپس تغییرات شیب محاسبه میشود.
|
||||
3. هر جایی که افت شیب ناگهان متوقف شود، همان نقطه elbow در نظر گرفته میشود.
|
||||
|
||||
یعنی در عمل:
|
||||
|
||||
- ابتدا `slopes` محاسبه میشود
|
||||
- سپس اختلاف شیبها بررسی میشود
|
||||
- بیشترین تغییر شیب به عنوان elbow انتخاب میشود
|
||||
|
||||
خروجی:
|
||||
|
||||
- `optimal_k`
|
||||
|
||||
---
|
||||
|
||||
## 6.3) تولید centroidهای نهایی
|
||||
|
||||
بعد از پیدا شدن `optimal_k`:
|
||||
|
||||
- مدل `KMeans` همان `K` نهایی انتخاب میشود
|
||||
- مختصات مراکز خوشهها (`cluster_centers_`) گرفته میشود
|
||||
- از فضای متری به `lat/lon` تبدیل میشود
|
||||
- در `centroid_points` ذخیره میشود
|
||||
|
||||
نمونه centroid:
|
||||
|
||||
```json
|
||||
{
|
||||
"sub_block_code": "sub-block-1",
|
||||
"centroid_lat": 35.689321,
|
||||
"centroid_lon": 51.389789
|
||||
}
|
||||
```
|
||||
|
||||
این centroidها در عمل همان مراکز بخشهای کوچکتر زمین هستند.
|
||||
|
||||
---
|
||||
|
||||
## 7) تولید و ذخیره نمودار Elbow
|
||||
|
||||
### تابع
|
||||
|
||||
- `render_elbow_plot()`
|
||||
|
||||
### منطق
|
||||
|
||||
پس از محاسبه `inertia_curve` و `optimal_k`:
|
||||
|
||||
1. نمودار `K` در برابر `SSE` رسم میشود
|
||||
2. نقطه elbow با رنگ قرمز مشخص میشود
|
||||
3. تصویر به صورت PNG در `BytesIO` ذخیره میشود
|
||||
4. با `ContentFile` به `ImageField` مدل `BlockSubdivision` داده میشود
|
||||
|
||||
### نکته مهم حافظه
|
||||
|
||||
برای جلوگیری از memory leak:
|
||||
|
||||
- از backend غیرتعاملی `Agg` استفاده میشود
|
||||
- بعد از ذخیره تصویر، `plt.close(fig)` اجرا میشود
|
||||
- buffer هم بسته میشود
|
||||
|
||||
این برای پردازشهای همزمان سرور ضروری است.
|
||||
|
||||
---
|
||||
|
||||
## 8) جریان کامل `POST /api/soil-data/`
|
||||
|
||||
این endpoint الان مهمترین ورودی subdivision است.
|
||||
|
||||
### ورودیهای قابل پشتیبانی
|
||||
|
||||
- `lat`
|
||||
- `lon`
|
||||
- `block_count`
|
||||
- `block_code`
|
||||
- `farm_boundary`
|
||||
|
||||
### سناریوی اجرا
|
||||
|
||||
#### مرحله 1: اعتبارسنجی ورودی
|
||||
|
||||
سریالایزر `SoilDataRequestSerializer` داده را validate میکند.
|
||||
|
||||
#### مرحله 2: پیدا کردن یا ساخت location
|
||||
|
||||
بر اساس `lat/lon`:
|
||||
|
||||
- اگر location وجود نداشته باشد ساخته میشود
|
||||
- اگر وجود داشته باشد از همان رکورد استفاده میشود
|
||||
|
||||
#### مرحله 3: آپدیت ساختار اولیه بلوکها
|
||||
|
||||
اگر `block_count` فرق کرده باشد:
|
||||
|
||||
- `block_layout` دوباره با `set_input_block_count()` ساخته میشود
|
||||
|
||||
#### مرحله 4: انتخاب boundary برای subdivision
|
||||
|
||||
اولویت:
|
||||
|
||||
1. `farm_boundary` ارسالی در request
|
||||
2. اگر نبود، `location.farm_boundary` ذخیرهشده
|
||||
|
||||
#### مرحله 5: اجرای subdivision فقط در صورت نیاز
|
||||
|
||||
تابع:
|
||||
|
||||
- `create_or_get_block_subdivision()`
|
||||
|
||||
اگر رکورد `(location, block_code)` از قبل وجود داشته باشد:
|
||||
|
||||
- هیچ پردازش جدیدی اجرا نمیشود
|
||||
- همان رکورد قبلی برگردانده میشود
|
||||
|
||||
اگر وجود نداشته باشد:
|
||||
|
||||
- grid ساخته میشود
|
||||
- KMeans اجرا میشود
|
||||
- elbow پیدا میشود
|
||||
- centroidها ساخته میشوند
|
||||
- نمودار elbow ساخته میشود
|
||||
- همه چیز در `BlockSubdivision` ذخیره میشود
|
||||
- `block_layout` با `sub_blocks` sync میشود
|
||||
|
||||
#### مرحله 6: response
|
||||
|
||||
خروجی شامل اینهاست:
|
||||
|
||||
- اطلاعات `SoilLocation`
|
||||
- `farm_boundary`
|
||||
- `block_layout`
|
||||
- `block_subdivisions`
|
||||
- `depths`
|
||||
|
||||
فیلد `source` در response:
|
||||
|
||||
- `created` اگر location یا subdivision جدید ساخته شده باشد
|
||||
- `database` اگر قبلاً وجود داشته باشد
|
||||
|
||||
---
|
||||
|
||||
## 9) جریان کامل `GET /api/soil-data/`
|
||||
|
||||
این endpoint الان فقط برای read استفاده میشود.
|
||||
|
||||
### ورودی
|
||||
|
||||
- `lat`
|
||||
- `lon`
|
||||
- `block_code` اختیاری
|
||||
|
||||
### رفتار
|
||||
|
||||
- location را از دیتابیس پیدا میکند
|
||||
- subdivisionهای ذخیرهشده را میخواند
|
||||
- هیچ الگوریتمی را اجرا نمیکند
|
||||
- هیچ `KMeans` یا پردازش هندسی انجام نمیدهد
|
||||
|
||||
### پاسخ
|
||||
|
||||
داده ذخیرهشده را با `source = database` برمیگرداند.
|
||||
|
||||
اگر location پیدا نشود:
|
||||
|
||||
- `404`
|
||||
|
||||
---
|
||||
|
||||
## 10) نقش `serializers.py`
|
||||
|
||||
### `SoilDataRequestSerializer`
|
||||
|
||||
ورودی endpoint اصلی را مدیریت میکند:
|
||||
|
||||
- `lat`
|
||||
- `lon`
|
||||
- `block_count`
|
||||
- `block_code`
|
||||
- `farm_boundary`
|
||||
|
||||
### `SoilLocationResponseSerializer`
|
||||
|
||||
خروجی location را برمیگرداند:
|
||||
|
||||
- `id`
|
||||
- `lat`
|
||||
- `lon`
|
||||
- `input_block_count`
|
||||
- `farm_boundary`
|
||||
- `block_layout`
|
||||
- `block_subdivisions`
|
||||
- `depths`
|
||||
|
||||
### `BlockSubdivisionSerializer`
|
||||
|
||||
خروجی subdivision را برمیگرداند:
|
||||
|
||||
- `block_code`
|
||||
- `chunk_size_sqm`
|
||||
- `grid_points`
|
||||
- `centroid_points`
|
||||
- `grid_point_count`
|
||||
- `centroid_count`
|
||||
- `elbow_plot`
|
||||
- `status`
|
||||
- `metadata`
|
||||
- `created_at`
|
||||
- `updated_at`
|
||||
|
||||
---
|
||||
|
||||
## 11) نقش `block_layout` در کنار `BlockSubdivision`
|
||||
|
||||
در معماری فعلی دو سطح ذخیرهسازی داریم:
|
||||
|
||||
### 11.1) `BlockSubdivision`
|
||||
|
||||
منبع اصلی و canonical برای subdivision
|
||||
|
||||
### 11.2) `block_layout`
|
||||
|
||||
خلاصهای از نتیجه subdivision برای مصرف سریعتر در response و ساختار کلی location
|
||||
|
||||
یعنی:
|
||||
|
||||
- داده دقیق در `BlockSubdivision` است
|
||||
- خلاصه آن در `block_layout.blocks[].sub_blocks` قرار میگیرد
|
||||
|
||||
---
|
||||
|
||||
## 12) وضعیت فعلی بخشهای قدیمیتر اپ
|
||||
|
||||
## 12.1) `tasks.py`
|
||||
|
||||
این فایل هنوز وجود دارد و برای fetch داده خاک به صورت قدیمی استفاده میشود، اما در مسیر subdivision فعلی نقشی ندارد.
|
||||
|
||||
## 12.2) `soil_adapters.py`
|
||||
|
||||
این فایل adapterهای داده خاک را نگه میدارد و فعلاً برای subdivision استفاده نمیشود.
|
||||
|
||||
## 12.3) `remote_sensing.py`
|
||||
|
||||
منطق سنجشازدور را نگه میدارد و هنوز مستقیماً به subdivision وصل نشده است.
|
||||
|
||||
## 12.4) `ndvi.py`
|
||||
|
||||
برای endpoint مربوط به NDVI استفاده میشود و فعلاً از centroidهای subdivision استفاده نمیکند.
|
||||
|
||||
---
|
||||
|
||||
## 13) وابستگیهای جدید
|
||||
|
||||
برای عملکرد فعلی subdivision این dependencyها لازم هستند:
|
||||
|
||||
- `scikit-learn`
|
||||
- `matplotlib`
|
||||
- `Pillow`
|
||||
- `numpy`
|
||||
|
||||
### دلیل هرکدام
|
||||
|
||||
- `scikit-learn`: اجرای `KMeans`
|
||||
- `matplotlib`: رسم elbow plot
|
||||
- `Pillow`: پشتیبانی از `ImageField`
|
||||
- `numpy`: وابستگی پایه `scikit-learn`
|
||||
|
||||
---
|
||||
|
||||
## 14) migrationهای مهم مرتبط با ساختار فعلی
|
||||
|
||||
- `0008_soillocation_block_layout.py`
|
||||
- اضافه شدن `input_block_count`
|
||||
- اضافه شدن `block_layout`
|
||||
|
||||
- `0009_blocksubdivision.py`
|
||||
- اضافه شدن مدل `BlockSubdivision`
|
||||
|
||||
- `0010_blocksubdivision_elbow_plot.py`
|
||||
- اضافه شدن فیلد `elbow_plot`
|
||||
|
||||
---
|
||||
|
||||
## 15) محدودیتهای فعلی
|
||||
|
||||
چند محدودیت مهم در پیادهسازی فعلی وجود دارد:
|
||||
|
||||
- subdivision فعلاً بر اساس هندسه و خوشهبندی نقاط انجام میشود، نه بر اساس داده واقعی خاک یا NDVI
|
||||
- برای هر `block_code` فرض میشود یک مرز مستقل از بیرون داده میشود
|
||||
- هنوز برای هر `sub_block` رکورد location مستقل ساخته نمیشود
|
||||
- هنوز داده خاک، هوا و NDVI برای centroidهای جدید به صورت جداگانه fetch نمیشود
|
||||
- elbow detection فعلی heuristic-based است و هنوز نسخه پیشرفتهتر آماری ندارد
|
||||
|
||||
---
|
||||
|
||||
## 16) تستهای مرتبط
|
||||
|
||||
### `location_data/test_block_subdivision.py`
|
||||
|
||||
این تستها بررسی میکنند:
|
||||
|
||||
- elbow detection کار میکند
|
||||
- payload subdivision ساخته میشود
|
||||
- grid points و centroid points خروجی دارند
|
||||
|
||||
### `location_data/test_soil_api.py`
|
||||
|
||||
این تستها بررسی میکنند:
|
||||
|
||||
- `POST` subdivision جدید میسازد
|
||||
- `GET` فقط داده ذخیرهشده را برمیگرداند
|
||||
- الگوریتم در `GET` دوباره اجرا نمیشود
|
||||
|
||||
---
|
||||
|
||||
## 17) جمعبندی معماری فعلی
|
||||
|
||||
در وضعیت فعلی، `location_data` این معماری را دارد:
|
||||
|
||||
### لایه 1: Location پایه
|
||||
|
||||
- `SoilLocation`
|
||||
- `farm_boundary`
|
||||
- `block_layout`
|
||||
|
||||
### لایه 2: Subdivision هوشمند
|
||||
|
||||
- `BlockSubdivision`
|
||||
- grid generation
|
||||
- KMeans
|
||||
- elbow detection
|
||||
- centroid generation
|
||||
- elbow plot generation
|
||||
|
||||
### لایه 3: دادههای مکمل
|
||||
|
||||
- `SoilDepthData`
|
||||
- `NdviObservation`
|
||||
- بخشهای legacy مثل `tasks.py`
|
||||
|
||||
در نتیجه، اپ الان میتواند:
|
||||
|
||||
- یک بلوک با مرز مشخص بگیرد
|
||||
- آن را به نقاط شبکهای خرد کند
|
||||
- تعداد بهینه بخشها را با KMeans + Elbow پیدا کند
|
||||
- centroidهای نهایی را ذخیره کند
|
||||
- نمودار elbow را ذخیره کند
|
||||
- و در درخواستهای بعدی فقط همان نتیجه ذخیرهشده را بدون پردازش مجدد برگرداند
|
||||
|
||||
---
|
||||
|
||||
## 18) پیشنهاد برای مراحل بعدی
|
||||
|
||||
اگر در مرحله بعد بخواهی این ساختار را توسعه بدهی، منطقیترین قدمها اینها هستند:
|
||||
|
||||
1. ساخت endpoint مستقل برای subdivision هر block
|
||||
2. اتصال هر centroid به fetch داده خاک و هوا
|
||||
3. ساخت رکورد مستقل برای هر `sub_block`
|
||||
4. استفاده از NDVI یا داده سنسور برای تعیین `K` یا وزندهی خوشهها
|
||||
5. نمایش مستقیم `elbow_plot` با URL کامل media
|
||||
|
||||
@@ -0,0 +1,972 @@
|
||||
# مستند کامل اپ `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 تبدیل شده است.
|
||||
Reference in New Issue
Block a user