diff --git a/CROP_ZONING_APIS.md b/CROP_ZONING_APIS.md new file mode 100644 index 0000000..63b994e --- /dev/null +++ b/CROP_ZONING_APIS.md @@ -0,0 +1,544 @@ +# مستندات APIهای زون‌بندی کشت (Crop Zoning) + +این سند تمام APIهای مورد نیاز برای صفحه **Crop Zoning** را شرح می‌دهد: ورودی‌ها، خروجی‌ها، محصولات، رنگ‌ها، مساحت کلی و دیتای هر بخش زمین به صورت جداگانه. + +**مسیر صفحه:** `(dashboard)/(private)/crop-zoning` +**کامپوننت اصلی:** `CropZoningWrapper` + +--- + +## نمای کلی و جریان درخواست‌ها + +``` +۱. GET area → منطقهٔ ثابت (کاربر امکان رسم ندارد) +۲. GET products → لیست محصولات و رنگ‌ها +۳. POST zones/initial → ارسال محدودهٔ مربع‌ها → دیتای محصولات پیشنهادی (نقشه + tooltip) +۴. POST zones/water-need → ارسال محدودهٔ مربع‌ها → نیاز آبی هر منطقه +۵. POST zones/soil-quality → ارسال محدودهٔ مربع‌ها → کیفیت خاک هر منطقه +۶. POST zones/cultivation-risk → ارسال محدودهٔ مربع‌ها → ریسک کشت هر منطقه +۷. GET zone/:zoneId → کلیک روی مربع → دیتای تکمیلی (پنل جزئیات: reason, criteria, ...) +``` + +| ردیف | API | هدف | +|------|-----|------| +| ۱ | **منطقهٔ اولیه** | دریافت منطقهٔ زمین به صورت GeoJSON؛ کاربر نمی‌تواند چیزی رسم کند | +| ۲ | **لیست محصولات و رنگ‌ها** | دریافت محصولات قابل کشت به همراه رنگ نمایش و لیبل فارسی | +| ۳ | **دیتای اولیه زون‌ها (محصولات)** | ارسال محدودهٔ مربع‌ها، دریافت محصول پیشنهادی برای نقشه و tooltip | +| ۴ | **نیاز آبی** | ارسال محدودهٔ مربع‌ها، دریافت نیاز آبی هر منطقه برای لایهٔ نیاز آبی | +| ۵ | **کیفیت خاک** | ارسال محدودهٔ مربع‌ها، دریافت کیفیت خاک هر منطقه برای لایهٔ کیفیت خاک | +| ۶ | **ریسک کشت** | ارسال محدودهٔ مربع‌ها، دریافت ریسک کشت هر منطقه برای لایهٔ ریسک کشت | +| ۷ | **دیتای تکمیلی زون** | با کلیک روی هر مربع، دریافت دیتای جزئیات (دلیل، معیارها، نمودار) | + +--- + +## ۰. API منطقهٔ اولیه (Area) + +منطقهٔ ثابت زمین که از بک‌اند دریافت می‌شود. کاربر امکان رسم یا ویرایش منطقه را ندارد. + +### ۰.۱ مشخصات + +- **متد:** `GET` +- **آدرس پیشنهادی:** `GET /api/crop-zoning/area/` +- **هدف:** دریافت polygon منطقهٔ زمین برای نمایش روی نقشه. + +### ۰.۲ ورودی (Request) + +بدون پارامتر. + +### ۰.۳ خروجی (Response Body) + +```json +{ + "status": "success", + "data": { + "area": { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [51.38, 35.68], + [51.40, 35.68], + [51.40, 35.70], + [51.38, 35.70], + [51.38, 35.68] + ] + ] + } + } + } +} +``` + +- **مختصات:** `[longitude, latitude]` طبق استاندارد GeoJSON + +--- + +## ۱. API لیست محصولات و رنگ‌ها + +برای نمایش راهنمای رنگ‌ها (Legend) و dropdown انتخاب محصول در پنل جزئیات هر زون. + +### ۱.۱ مشخصات + +- **متد:** `GET` +- **آدرس پیشنهادی:** `GET /api/crop-zoning/products/` یا `GET /api/crops/` +- **هدف:** دریافت لیست محصولات قابل کشت با رنگ و لیبل نمایشی. + +### ۱.۲ ورودی (Request) + +بدون پارامتر یا با پارامترهای اختیاری: + +| پارامتر | نوع | اجباری | توضیح | +|---------|-----|--------|--------| +| `locale` | string | خیر | کد زبان برای لیبل‌ها (مثلاً `fa`, `en`) | + +### ۱.۳ خروجی (Response) + +```json +{ + "status": "success", + "data": { + "products": [ + { + "id": "wheat", + "label": "گندم", + "color": "#6bcb77" + }, + { + "id": "canola", + "label": "کلزا", + "color": "#ffd93d" + }, + { + "id": "saffron", + "label": "زعفران", + "color": "#9b59b6" + } + ] + } +} +``` + +**ساختار هر محصول:** + +| فیلد | نوع | اجباری | توضیح | +|------|-----|--------|--------| +| `id` | string | بله | شناسهٔ یکتا (مثلاً `wheat`, `canola`, `saffron`) | +| `label` | string | بله | نام نمایشی به زبان کاربر | +| `color` | string | بله | رنگ hex برای نمایش روی نقشه و Legend | + +--- + +## ۲. API دیتای اولیه زون‌ها + +با رسم منطقهٔ زمین، فرانت **محدودهٔ همهٔ مربع‌ها** (گرید داخل polygon) را ارسال می‌کند و **دیتای اولیه** هر مربع را دریافت می‌کند — برای نمایش نقشه، رنگ‌بندی، و **هاور/tooltip**. این دیتا شامل `reason` و `criteria` **نیست**. + +### ۲.۱ مشخصات + +- **متد:** `POST` +- **آدرس پیشنهادی:** `POST /api/crop-zoning/zones/initial/` +- **هدف:** ارسال محدودهٔ مربع‌ها، دریافت دیتای اولیه برای نقشه، هاور و tooltip. + +### ۲.۲ ورودی (Request Body) + +فرانت ابتدا با Turf.js از روی polygon منطقه گرید می‌سازد، سپس `FeatureCollection` همهٔ polygonهای مربع‌ها را ارسال می‌کند. + +| فیلد | نوع | اجباری | توضیح | +|------|-----|--------|--------| +| `zones` | GeoJSON FeatureCollection | بله | محدودهٔ هر مربع به صورت Polygon؛ ترتیب index با پاسخ یکسان است | +| `products` | string[] | خیر | لیست محصولات مدنظر؛ در صورت عدم ارسال از همهٔ محصولات استفاده شود | + +**ساختار `zones` (محدودهٔ همهٔ مربع‌ها):** + +```json +{ + "zones": { + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [51.38, 35.68], + [51.3815, 35.68], + [51.3815, 35.6815], + [51.38, 35.6815], + [51.38, 35.68] + ] + ] + }, + "properties": { "index": 0 } + }, + { + "type": "Feature", + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [51.3815, 35.68], + [51.383, 35.68], + [51.383, 35.6815], + [51.3815, 35.6815], + [51.3815, 35.68] + ] + ] + }, + "properties": { "index": 1 } + } + ] + }, + "products": ["wheat", "canola", "saffron"] +} +``` + +- **مختصات:** `[longitude, latitude]` طبق استاندارد GeoJSON +- **index:** در `properties` هر feature برای تطابق با پاسخ (اختیاری؛ در صورت نبودن از ترتیب آرایه استفاده شود) + +### ۲.۳ خروجی (Response Body) — دیتای اولیه + +فقط فیلدهای لازم برای **نقشه**، **هاور** و **tooltip** (نمایش هنگام عبور ماوس روی هر مربع)؛ بدون `reason` و `criteria`. + +```json +{ + "status": "success", + "data": { + "total_area_hectares": 23.45, + "total_area_sqm": 234500, + "zone_count": 3, + "zones": [ + { + "zoneId": "zone-0", + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [51.38, 35.68], + [51.3815, 35.68], + [51.3815, 35.6815], + [51.38, 35.6815], + [51.38, 35.68] + ] + ] + }, + "crop": "wheat", + "matchPercent": 85, + "waterNeed": "۴۵۰۰-۵۵۰۰ m³/ha", + "estimatedProfit": "۱۵-۲۵ میلیون/هکتار" + }, + { + "zoneId": "zone-1", + "geometry": { "type": "Polygon", "coordinates": [...] }, + "crop": "canola", + "matchPercent": 78, + "waterNeed": "۵۰۰۰-۶۰۰۰ m³/ha", + "estimatedProfit": "۲۰-۳۵ میلیون/هکتار" + } + ] + } +} +``` + +**ساختار دیتای اولیه هر زون (هم برای نقشه هم برای هاور/tooltip):** + +| فیلد | نوع | اجباری | توضیح | +|------|-----|--------|--------| +| `zoneId` | string | بله | شناسهٔ یکتا برای درخواست دیتای تکمیلی | +| `geometry` | Polygon | بله | هندسهٔ همان مربع ارسالی | +| `crop` | string \| null | خیر | محصول پیشنهادی؛ اگر `null`/خالی/`uncultivable` باشد → زون **غیرقابل کشت** و رنگ خاکستری | +| `matchPercent` | number | خیر | درصد تطابق (هاور/tooltip) | +| `waterNeed` | string | خیر | نیاز آبی (هاور/tooltip) | +| `estimatedProfit` | string | خیر | سود تخمینی (هاور/tooltip) | + +**زون غیرقابل کشت:** اگر برای مربعی اطلاعاتی نیاید یا `crop` خالی/`null`/`uncultivable` باشد، آن مربع خاکستری نمایش داده شده و در tooltip «غیر قابل کشت» نشان داده می‌شود. کلیک روی آن پنل جزئیات باز نمی‌شود. + +**نکته:** این فیلدها هنگام **هاور** روی مربع در tooltip نمایش داده می‌شوند؛ نیازی به درخواست جداگانه برای tooltip نیست. + +--- + +## ۲.۱ API نیاز آبی (Water Need) + +نیاز آبی هر منطقه را بر اساس محدودهٔ مربع‌ها برمی‌گرداند. با تغییر لایه به «نیاز آبی» در LayerControl، فرانت این API را صدا می‌زند و نقشه و Legend را به‌روزرسانی می‌کند. + +### مشخصات + +- **متد:** `POST` +- **آدرس پیشنهادی:** `POST /api/crop-zoning/zones/water-need/` +- **هدف:** دریافت نیاز آبی هر منطقه برای نمایش روی نقشه در لایهٔ نیاز آبی. + +### ورودی (Request Body) + +همان ساختار `POST zones/initial/`: + +| فیلد | نوع | اجباری | توضیح | +|------|-----|--------|--------| +| `zones` | GeoJSON FeatureCollection | بله | محدودهٔ هر مربع به صورت Polygon | + +### خروجی (Response Body) + +```json +{ + "status": "success", + "data": { + "zones": [ + { + "zoneId": "zone-0", + "geometry": { "type": "Polygon", "coordinates": [...] }, + "level": "low", + "value": "۳۰۰۰-۴۰۰۰ m³/ha", + "color": "#7dd3fc" + }, + { + "zoneId": "zone-1", + "geometry": { "type": "Polygon", "coordinates": [...] }, + "level": "medium", + "value": "۵۰۰۰-۶۰۰۰ m³/ha", + "color": "#0ea5e9" + } + ] + } +} +``` + +| فیلد | نوع | توضیح | +|------|-----|--------| +| `zoneId` | string | شناسهٔ زون | +| `geometry` | Polygon | هندسهٔ مربع | +| `level` | string | سطح: `low`, `medium`, `high` | +| `value` | string | مقدار نیاز آبی (مثلاً m³/ha) | +| `color` | string | رنگ hex برای نمایش | + +--- + +## ۲.۲ API کیفیت خاک (Soil Quality) + +کیفیت خاک هر منطقه را برمی‌گرداند. با تغییر لایه به «کیفیت خاک»، فرانت این API را صدا می‌زند. + +### مشخصات + +- **متد:** `POST` +- **آدرس پیشنهادی:** `POST /api/crop-zoning/zones/soil-quality/` + +### ورودی (Request Body) + +همان `zones` (FeatureCollection). + +### خروجی (Response Body) + +```json +{ + "status": "success", + "data": { + "zones": [ + { + "zoneId": "zone-0", + "geometry": { "type": "Polygon", "coordinates": [...] }, + "level": "low", + "score": 35, + "color": "#f87171" + }, + { + "zoneId": "zone-1", + "geometry": { "type": "Polygon", "coordinates": [...] }, + "level": "high", + "score": 85, + "color": "#22c55e" + } + ] + } +} +``` + +| فیلد | نوع | توضیح | +|------|-----|--------| +| `level` | string | سطح: `low`, `medium`, `high` | +| `score` | number | امتیاز ۰–۱۰۰ | +| `color` | string | رنگ hex | + +--- + +## ۲.۳ API ریسک کشت (Cultivation Risk) + +ریسک کشت هر منطقه را برمی‌گرداند. با تغییر لایه به «ریسک کشت»، فرانت این API را صدا می‌زند. + +### مشخصات + +- **متد:** `POST` +- **آدرس پیشنهادی:** `POST /api/crop-zoning/zones/cultivation-risk/` + +### ورودی و خروجی + +ورودی: همان `zones` (FeatureCollection). + +خروجی نمونه: + +```json +{ + "status": "success", + "data": { + "zones": [ + { + "zoneId": "zone-0", + "geometry": { "type": "Polygon", "coordinates": [...] }, + "level": "low", + "color": "#22c55e" + }, + { + "zoneId": "zone-1", + "geometry": { "type": "Polygon", "coordinates": [...] }, + "level": "high", + "color": "#ef4444" + } + ] + } +} +``` + +| فیلد | نوع | توضیح | +|------|-----|--------| +| `level` | string | سطح: `low`, `medium`, `high` | +| `color` | string | رنگ hex | + +**نکته:** برای هر لایه (نیاز آبی، کیفیت خاک، ریسک کشت) فرانت یک **درخواست جداگانه** ارسال می‌کند و نقشه و Legend متناسب با همان لایه به‌روزرسانی می‌شوند. + +--- + +## ۳. API دیتای تکمیلی زون (با کلیک روی مربع) + +وقتی کاربر روی یک مربع کلیک می‌کند، فرانت با `zoneId` دیتای **تکمیلی** را درخواست می‌کند — برای نمایش پنل جزئیات: دلیل پیشنهاد، معیارها، نمودار راداری. + +### ۳.۱ مشخصات + +- **متد:** `GET` +- **آدرس پیشنهادی:** `GET /api/crop-zoning/zones/:zoneId/details/` +- **هدف:** دریافت دیتای تکمیلی یک زون برای پنل جزئیات. + +### ۳.۲ ورودی (Request) + +| پارامتر | محل | نوع | اجباری | توضیح | +|---------|------|-----|--------|--------| +| `zoneId` | path | string | بله | شناسهٔ زون (مثلاً `zone-0`) | + +**مثال:** `GET /api/crop-zoning/zones/zone-0/details/` + +### ۳.۳ خروجی (Response Body) + +```json +{ + "status": "success", + "data": { + "zoneId": "zone-0", + "crop": "wheat", + "matchPercent": 85, + "waterNeed": "۴۵۰۰-۵۵۰۰ m³/ha", + "estimatedProfit": "۱۵-۲۵ میلیون/هکتار", + "reason": "دمای مناسب، خاک حاصلخیز، دسترسی به آب کافی", + "criteria": [ + { "name": "دما", "value": 82 }, + { "name": "بارش", "value": 75 }, + { "name": "خاک", "value": 88 }, + { "name": "آب", "value": 70 } + ], + "area_hectares": 2.25 + } +} +``` + +**فیلدهای دیتای تکمیلی:** + +| فیلد | نوع | اجباری | توضیح | +|------|-----|--------|--------| +| `zoneId` | string | بله | همان zoneId درخواست | +| `crop` | string | بله | محصول پیشنهادی | +| `matchPercent` | number | بله | درصد تطابق | +| `waterNeed` | string | بله | نیاز آبی | +| `estimatedProfit` | string | بله | سود تخمینی | +| `reason` | string | بله | **فقط در دیتای تکمیلی** — دلیل پیشنهاد محصول | +| `criteria` | object[] | بله | **فقط در دیتای تکمیلی** — معیارها برای نمودار راداری | +| `area_hectares` | number | خیر | مساحت این زون بر حسب هکتار | + +### ۳.۴ ساختار `criteria` + +| فیلد | نوع | توضیح | +|------|-----|--------| +| `name` | string | نام معیار (دما، بارش، خاک، آب) | +| `value` | number | امتیاز ۰–۱۰۰ | + +--- + +## ۴. مساحت کلی (Total Area) + +در پاسخ **API دیتای اولیه زون‌ها** برمی‌گردد: + +| فیلد | نوع | توضیح | +|------|-----|--------| +| `total_area_hectares` | number | مساحت کل منطقه بر حسب هکتار | +| `total_area_sqm` | number | مساحت کل بر حسب متر مربع | + +--- + +## ۵. خلاصهٔ ساختار‌های مورد نیاز فرانت + +### دیتای اولیه زون (برای نقشه و هاور/tooltip) + +```ts +interface ZoneInitialData { + zoneId: string + geometry: Polygon + crop: string + matchPercent: number + waterNeed: string + estimatedProfit: string +} +``` + +### دیتای تکمیلی زون (برای پنل جزئیات — پس از کلیک) + +```ts +interface ZoneDetailData { + zoneId: string + crop: string + matchPercent: number + waterNeed: string + estimatedProfit: string + reason: string + criteria: { name: string; value: number }[] + area_hectares?: number +} +``` + +### محصولات و رنگ‌ها (پیش‌فرض فرانت) + +```ts +const CROP_COLORS: Record = { + wheat: '#6bcb77', + canola: '#ffd93d', + saffron: '#9b59b6' +} +``` + +--- + +## ۶. جریان فرانت با APIها + +1. **لود صفحه:** `GET /api/crop-zoning/products/` → لیست محصولات و رنگ‌ها. +2. **رسم منطقه / بهینه‌سازی:** فرانت با Turf از polygon منطقه گرید می‌سازد → `POST /api/crop-zoning/zones/initial/` با `zones` (FeatureCollection) → نقشه و tooltip با دیتای محصولات رسم می‌شود. +3. **تغییر لایه در LayerControl:** برای هر لایه یک درخواست جداگانه ارسال می‌شود: + - محصولات پیشنهادی: `POST zones/initial/` (در مرحلهٔ ۲) + - نیاز آبی: `POST zones/water-need/` → نقشه و Legend به‌روزرسانی می‌شوند + - کیفیت خاک: `POST zones/soil-quality/` → نقشه و Legend به‌روزرسانی می‌شوند + - ریسک کشت: `POST zones/cultivation-risk/` → نقشه و Legend به‌روزرسانی می‌شوند +4. **کلیک روی مربع:** `GET /api/crop-zoning/zones/{zoneId}/details/` → دیتای تکمیلی → پنل جزئیات باز می‌شود (reason, criteria, نمودار راداری). + +--- + +## ۷. وضعیت فعلی و نیازمندی‌ها + +- در حال حاضر زون‌بندی با **دیتای ماک** و الگوریتم محلی (`createZonedGrid` در `cropZoningUtils.ts`) کار می‌کند. +- برای اتصال به بک‌اند، لازم است: + 1. سرویس `cropZoningService` با سه endpoint: `getProducts()`, `getZonesInitial(zones)`, `getZoneDetails(zoneId)` ایجاد شود. + 2. در `CropZoningMap` به جای `createZonedGrid` ابتدا گرید با Turf ساخته شود، سپس `zones` به API ارسال و پاسخ برای رسم استفاده شود. + 3. در `onZoneClick` قبل از باز کردن پنل، `getZoneDetails(zoneId)` صدا زده شود و دیتای تکمیلی به `ZoneDetailPanel` پاس داده شود. + 4. مساحت کلی (`total_area_hectares`) در پاسخ initial در UI نمایش داده شود. diff --git a/crop_zoning/__init__.py b/crop_zoning/__init__.py index 8b13789..e69de29 100644 --- a/crop_zoning/__init__.py +++ b/crop_zoning/__init__.py @@ -1 +0,0 @@ - diff --git a/crop_zoning/mock_data.py b/crop_zoning/mock_data.py index 2168a10..5289fbd 100644 --- a/crop_zoning/mock_data.py +++ b/crop_zoning/mock_data.py @@ -1,116 +1,354 @@ """ Static mock data for Crop Zoning API. -Matches API_RESPONSE_SPEC.md. No database, no dynamic values. +Matches CROP_ZONING_APIS.md. No database, no dynamic values. """ -# Response for POST optimize: GeoJSON FeatureCollection (API_RESPONSE_SPEC §1) -OPTIMIZE_ZONING_RESPONSE = { - "type": "FeatureCollection", - "features": [ +# --------------------------------------------------------------------------- +# GET /api/crop-zoning/area/ +# منطقهٔ ثابت — کاربر امکان رسم ندارد +# --------------------------------------------------------------------------- + +AREA_RESPONSE_DATA = { + "area": { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [51.38, 35.68], + [51.405, 35.672], + [51.41, 35.695], + [51.385, 35.71], + [51.365, 35.688], + [51.38, 35.68], + ] + ], + }, + } +} + +# --------------------------------------------------------------------------- +# GET /api/crop-zoning/products/ +# --------------------------------------------------------------------------- + +PRODUCTS_RESPONSE_DATA = { + "products": [ + {"id": "wheat", "label": "گندم", "color": "#6bcb77"}, + {"id": "canola", "label": "کلزا", "color": "#ffd93d"}, + {"id": "saffron", "label": "زعفران", "color": "#9b59b6"}, + ] +} + +# --------------------------------------------------------------------------- +# POST /api/crop-zoning/zones/initial/ +# دیتای اولیه برای نقشه و هاور/tooltip — بدون reason و criteria +# --------------------------------------------------------------------------- + +ZONES_INITIAL_RESPONSE_DATA = { + "total_area_hectares": 23.45, + "total_area_sqm": 234500, + "zone_count": 3, + "zones": [ { - "type": "Feature", + "zoneId": "zone-0", "geometry": { "type": "Polygon", "coordinates": [ [ [51.38, 35.68], - [51.381, 35.68], - [51.381, 35.681], - [51.38, 35.681], + [51.3815, 35.68], + [51.3815, 35.6815], + [51.38, 35.6815], [51.38, 35.68], ] ], }, - "properties": { - "zoneId": "zone-0", - "crop": "wheat", - "matchPercent": 78, - "waterNeed": "۴۵۰۰-۵۵۰۰ m³/ha", - "estimatedProfit": "۱۵-۲۵ میلیون/هکتار", - "reason": "دمای مناسب، خاک حاصلخیز، دسترسی به آب کافی", - "criteria": [ - {"name": "دما", "value": 85}, - {"name": "بارش", "value": 72}, - {"name": "خاک", "value": 80}, - {"name": "آب", "value": 65}, - ], - }, + "crop": "wheat", + "matchPercent": 85, + "waterNeed": "۴۵۰۰-۵۵۰۰ m³/ha", + "estimatedProfit": "۱۵-۲۵ میلیون/هکتار", }, { - "type": "Feature", + "zoneId": "zone-1", "geometry": { "type": "Polygon", "coordinates": [ [ - [51.381, 35.68], - [51.382, 35.68], - [51.382, 35.681], - [51.381, 35.681], - [51.381, 35.68], + [51.3815, 35.68], + [51.383, 35.68], + [51.383, 35.6815], + [51.3815, 35.6815], + [51.3815, 35.68], ] ], }, - "properties": { - "zoneId": "zone-1", - "crop": "canola", - "matchPercent": 82, - "waterNeed": "۳۵۰۰-۴۵۰۰ m³/ha", - "estimatedProfit": "۲۰-۳۰ میلیون/هکتار", - "reason": "بارش کافی، خاک با بافت مناسب", - "criteria": [ - {"name": "دما", "value": 70}, - {"name": "بارش", "value": 88}, - {"name": "خاک", "value": 75}, - {"name": "آب", "value": 90}, - ], - }, + "crop": "canola", + "matchPercent": 78, + "waterNeed": "۵۰۰۰-۶۰۰۰ m³/ha", + "estimatedProfit": "۲۰-۳۵ میلیون/هکتار", }, { - "type": "Feature", + "zoneId": "zone-2", "geometry": { "type": "Polygon", "coordinates": [ [ - [51.382, 35.68], - [51.40, 35.68], - [51.40, 35.681], - [51.382, 35.681], - [51.382, 35.68], + [51.38, 35.6815], + [51.3815, 35.6815], + [51.3815, 35.683], + [51.38, 35.683], + [51.38, 35.6815], ] ], }, - "properties": { - "zoneId": "zone-2", - "crop": "saffron", - "matchPercent": 65, - "waterNeed": "۲۵۰۰-۳۵۰۰ m³/ha", - "estimatedProfit": "۸۰-۱۲۰ میلیون/هکتار", - "reason": "آب و هوای خشک و سرد مناسب زعفران", - "criteria": [ - {"name": "دما", "value": 60}, - {"name": "بارش", "value": 55}, - {"name": "خاک", "value": 85}, - {"name": "آب", "value": 50}, - ], - }, + "crop": "saffron", + "matchPercent": 92, + "waterNeed": "۳۰۰۰-۴۰۰۰ m³/ha", + "estimatedProfit": "۵۰-۱۵۰ میلیون/هکتار", }, ], } -# Response for GET initial region: GeoJSON Feature with Polygon (API_RESPONSE_SPEC §2) -INITIAL_REGION_RESPONSE = { - "type": "Feature", - "properties": {}, - "geometry": { - "type": "Polygon", - "coordinates": [ - [ - [51.38, 35.68], - [51.40, 35.68], - [51.40, 35.70], - [51.38, 35.70], - [51.38, 35.68], - ] +# --------------------------------------------------------------------------- +# POST /api/crop-zoning/zones/water-need/ +# نیاز آبی هر منطقه برای لایهٔ نیاز آبی +# --------------------------------------------------------------------------- + +ZONES_WATER_NEED_RESPONSE_DATA = { + "zones": [ + { + "zoneId": "zone-0", + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [51.38, 35.68], + [51.3815, 35.68], + [51.3815, 35.6815], + [51.38, 35.6815], + [51.38, 35.68], + ] + ], + }, + "level": "medium", + "value": "۴۵۰۰-۵۵۰۰ m³/ha", + "color": "#0ea5e9", + }, + { + "zoneId": "zone-1", + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [51.3815, 35.68], + [51.383, 35.68], + [51.383, 35.6815], + [51.3815, 35.6815], + [51.3815, 35.68], + ] + ], + }, + "level": "high", + "value": "۵۰۰۰-۶۰۰۰ m³/ha", + "color": "#0369a1", + }, + { + "zoneId": "zone-2", + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [51.38, 35.6815], + [51.3815, 35.6815], + [51.3815, 35.683], + [51.38, 35.683], + [51.38, 35.6815], + ] + ], + }, + "level": "low", + "value": "۳۰۰۰-۴۰۰۰ m³/ha", + "color": "#7dd3fc", + }, + ] +} + +# --------------------------------------------------------------------------- +# POST /api/crop-zoning/zones/soil-quality/ +# کیفیت خاک هر منطقه برای لایهٔ کیفیت خاک +# --------------------------------------------------------------------------- + +ZONES_SOIL_QUALITY_RESPONSE_DATA = { + "zones": [ + { + "zoneId": "zone-0", + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [51.38, 35.68], + [51.3815, 35.68], + [51.3815, 35.6815], + [51.38, 35.6815], + [51.38, 35.68], + ] + ], + }, + "level": "high", + "score": 88, + "color": "#22c55e", + }, + { + "zoneId": "zone-1", + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [51.3815, 35.68], + [51.383, 35.68], + [51.383, 35.6815], + [51.3815, 35.6815], + [51.3815, 35.68], + ] + ], + }, + "level": "medium", + "score": 62, + "color": "#eab308", + }, + { + "zoneId": "zone-2", + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [51.38, 35.6815], + [51.3815, 35.6815], + [51.3815, 35.683], + [51.38, 35.683], + [51.38, 35.6815], + ] + ], + }, + "level": "high", + "score": 95, + "color": "#22c55e", + }, + ] +} + +# --------------------------------------------------------------------------- +# POST /api/crop-zoning/zones/cultivation-risk/ +# ریسک کشت هر منطقه برای لایهٔ ریسک کشت +# --------------------------------------------------------------------------- + +ZONES_CULTIVATION_RISK_RESPONSE_DATA = { + "zones": [ + { + "zoneId": "zone-0", + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [51.38, 35.68], + [51.3815, 35.68], + [51.3815, 35.6815], + [51.38, 35.6815], + [51.38, 35.68], + ] + ], + }, + "level": "low", + "color": "#22c55e", + }, + { + "zoneId": "zone-1", + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [51.3815, 35.68], + [51.383, 35.68], + [51.383, 35.6815], + [51.3815, 35.6815], + [51.3815, 35.68], + ] + ], + }, + "level": "medium", + "color": "#f59e0b", + }, + { + "zoneId": "zone-2", + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [51.38, 35.6815], + [51.3815, 35.6815], + [51.3815, 35.683], + [51.38, 35.683], + [51.38, 35.6815], + ] + ], + }, + "level": "low", + "color": "#22c55e", + }, + ] +} + +# --------------------------------------------------------------------------- +# GET /api/crop-zoning/zones/:zoneId/details/ +# دیتای تکمیلی برای پنل جزئیات — شامل reason و criteria +# منطبق با createZonedGrid و MOCK_AREA_GEOJSON +# --------------------------------------------------------------------------- + +ZONE_DETAILS_BY_ID = { + "zone-0": { + "zoneId": "zone-0", + "crop": "wheat", + "matchPercent": 85, + "waterNeed": "۴۵۰۰-۵۵۰۰ m³/ha", + "estimatedProfit": "۱۵-۲۵ میلیون/هکتار", + "reason": "دمای مناسب، خاک حاصلخیز، دسترسی به آب کافی", + "criteria": [ + {"name": "دما", "value": 82}, + {"name": "بارش", "value": 75}, + {"name": "خاک", "value": 88}, + {"name": "آب", "value": 70}, ], + "area_hectares": 2.25, + }, + "zone-1": { + "zoneId": "zone-1", + "crop": "canola", + "matchPercent": 78, + "waterNeed": "۵۰۰۰-۶۰۰۰ m³/ha", + "estimatedProfit": "۲۰-۳۵ میلیون/هکتار", + "reason": "شرایط اقلیمی مساعد، نیاز آبی قابل تأمین", + "criteria": [ + {"name": "دما", "value": 75}, + {"name": "بارش", "value": 72}, + {"name": "خاک", "value": 80}, + {"name": "آب", "value": 78}, + ], + "area_hectares": 2.25, + }, + "zone-2": { + "zoneId": "zone-2", + "crop": "saffron", + "matchPercent": 92, + "waterNeed": "۳۰۰۰-۴۰۰۰ m³/ha", + "estimatedProfit": "۵۰-۱۵۰ میلیون/هکتار", + "reason": "ارتفاع و آب و هوای خشک مناسب، پتانسیل سود بالا", + "criteria": [ + {"name": "دما", "value": 90}, + {"name": "بارش", "value": 65}, + {"name": "خاک", "value": 95}, + {"name": "آب", "value": 85}, + ], + "area_hectares": 2.25, }, } diff --git a/crop_zoning/postman/crop_zoning.json b/crop_zoning/postman/crop_zoning.json index 4d7a6e0..eb4f42b 100644 --- a/crop_zoning/postman/crop_zoning.json +++ b/crop_zoning/postman/crop_zoning.json @@ -1,48 +1 @@ -{ - "info": { - "name": "Crop Zoning", - "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", - "description": "Crop Zoning API. POST optimize (GeoJSON FeatureCollection). GET initial-region (GeoJSON Feature). Static mock only. No database." - }, - "item": [ - { - "name": "Optimize zoning (POST)", - "request": { - "method": "POST", - "header": [{"key": "Content-Type", "value": "application/json"}], - "body": { - "mode": "raw", - "raw": "{\n \"type\": \"Feature\",\n \"geometry\": {\n \"type\": \"Polygon\",\n \"coordinates\": [[[51.38, 35.68], [51.40, 35.68], [51.40, 35.70], [51.38, 35.70], [51.38, 35.68]]]\n }\n}" - }, - "url": "{{baseUrl}}/api/crop-zoning/optimize/", - "description": "POST with GeoJSON Polygon (region). Returns static FeatureCollection. Input not processed." - }, - "response": [ - { - "name": "Success", - "status": "OK", - "code": 200, - "body": "{\n \"status\": \"success\",\n \"data\": {\n \"type\": \"FeatureCollection\",\n \"features\": [\n {\n \"type\": \"Feature\",\n \"geometry\": {\n \"type\": \"Polygon\",\n \"coordinates\": [[[51.38, 35.68], [51.381, 35.68], [51.381, 35.681], [51.38, 35.681], [51.38, 35.68]]]\n },\n \"properties\": {\n \"zoneId\": \"zone-0\",\n \"crop\": \"wheat\",\n \"matchPercent\": 78,\n \"waterNeed\": \"۴۵۰۰-۵۵۰۰ m³/ha\",\n \"estimatedProfit\": \"۱۵-۲۵ میلیون/هکتار\",\n \"reason\": \"دمای مناسب، خاک حاصلخیز، دسترسی به آب کافی\",\n \"criteria\": [{\"name\": \"دما\", \"value\": 85}, {\"name\": \"بارش\", \"value\": 72}, {\"name\": \"خاک\", \"value\": 80}, {\"name\": \"آب\", \"value\": 65}]\n }\n },\n {\n \"type\": \"Feature\",\n \"geometry\": {\n \"type\": \"Polygon\",\n \"coordinates\": [[[51.381, 35.68], [51.382, 35.68], [51.382, 35.681], [51.381, 35.681], [51.381, 35.68]]]\n },\n \"properties\": {\n \"zoneId\": \"zone-1\",\n \"crop\": \"canola\",\n \"matchPercent\": 82,\n \"waterNeed\": \"۳۵۰۰-۴۵۰۰ m³/ha\",\n \"estimatedProfit\": \"۲۰-۳۰ میلیون/هکتار\",\n \"reason\": \"بارش کافی، خاک با بافت مناسب\",\n \"criteria\": [{\"name\": \"دما\", \"value\": 70}, {\"name\": \"بارش\", \"value\": 88}, {\"name\": \"خاک\", \"value\": 75}, {\"name\": \"آب\", \"value\": 90}]\n }\n },\n {\n \"type\": \"Feature\",\n \"geometry\": {\n \"type\": \"Polygon\",\n \"coordinates\": [[[51.382, 35.68], [51.40, 35.68], [51.40, 35.681], [51.382, 35.681], [51.382, 35.68]]]\n },\n \"properties\": {\n \"zoneId\": \"zone-2\",\n \"crop\": \"saffron\",\n \"matchPercent\": 65,\n \"waterNeed\": \"۲۵۰۰-۳۵۰۰ m³/ha\",\n \"estimatedProfit\": \"۸۰-۱۲۰ میلیون/هکتار\",\n \"reason\": \"آب و هوای خشک و سرد مناسب زعفران\",\n \"criteria\": [{\"name\": \"دما\", \"value\": 60}, {\"name\": \"بارش\", \"value\": 55}, {\"name\": \"خاک\", \"value\": 85}, {\"name\": \"آب\", \"value\": 50}]\n }\n }\n ]\n }\n}" - } - ] - }, - { - "name": "Get initial region (GET)", - "request": { - "method": "GET", - "header": [{"key": "Content-Type", "value": "application/json"}], - "url": "{{baseUrl}}/api/crop-zoning/initial-region/", - "description": "GET initial map region as GeoJSON Feature with Polygon. Static mock only." - }, - "response": [ - { - "name": "Success", - "status": "OK", - "code": 200, - "body": "{\n \"status\": \"success\",\n \"data\": {\n \"type\": \"Feature\",\n \"properties\": {},\n \"geometry\": {\n \"type\": \"Polygon\",\n \"coordinates\": [[[51.38, 35.68], [51.40, 35.68], [51.40, 35.70], [51.38, 35.70], [51.38, 35.68]]]\n }\n }\n}" - } - ] - } - ], - "variable": [{"key": "baseUrl", "value": "http://localhost:8000"}] -} +{"info":{"name":"Crop Zoning","schema":"https://schema.getpostman.com/json/collection/v2.1.0/collection.json","description":"Crop Zoning API. GET area. GET products. POST zones/initial (crops). POST zones/water-need, soil-quality, cultivation-risk (layer data). GET zones/:zoneId/details (detail panel)."},"item":[{"name":"Get area (GET)","request":{"method":"GET","header":[{"key":"Content-Type","value":"application/json"}],"url":"{{baseUrl}}/api/crop-zoning/area/","description":"Returns fixed land area GeoJSON polygon for map. User cannot draw/edit."},"response":[{"name":"Success","status":"OK","code":200,"body":"{\n \"status\": \"success\",\n \"data\": {\n \"area\": {\n \"type\": \"Feature\",\n \"properties\": {},\n \"geometry\": {\n \"type\": \"Polygon\",\n \"coordinates\": [[[51.38, 35.68], [51.405, 35.672], [51.41, 35.695], [51.385, 35.71], [51.365, 35.688], [51.38, 35.68]]]\n }\n }\n }\n}"}]},{"name":"Get products (GET)","request":{"method":"GET","header":[{"key":"Content-Type","value":"application/json"}],"url":"{{baseUrl}}/api/crop-zoning/products/","description":"Returns static list of cultivable products (id, label, color) for Legend and zone detail panel."},"response":[{"name":"Success","status":"OK","code":200,"body":"{\n \"status\": \"success\",\n \"data\": {\n \"products\": [\n {\"id\": \"wheat\", \"label\": \"گندم\", \"color\": \"#6bcb77\"},\n {\"id\": \"canola\", \"label\": \"کلزا\", \"color\": \"#ffd93d\"},\n {\"id\": \"saffron\", \"label\": \"زعفران\", \"color\": \"#9b59b6\"}\n ]\n }\n}"}]},{"name":"Zones initial (POST)","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"body":{"mode":"raw","raw":"{\n \"zones\": {\n \"type\": \"FeatureCollection\",\n \"features\": [\n {\n \"type\": \"Feature\",\n \"geometry\": {\n \"type\": \"Polygon\",\n \"coordinates\": [[[51.38, 35.68], [51.3815, 35.68], [51.3815, 35.6815], [51.38, 35.6815], [51.38, 35.68]]]\n },\n \"properties\": {\"index\": 0}\n },\n {\n \"type\": \"Feature\",\n \"geometry\": {\n \"type\": \"Polygon\",\n \"coordinates\": [[[51.3815, 35.68], [51.383, 35.68], [51.383, 35.6815], [51.3815, 35.6815], [51.3815, 35.68]]]\n },\n \"properties\": {\"index\": 1}\n },\n {\n \"type\": \"Feature\",\n \"geometry\": {\n \"type\": \"Polygon\",\n \"coordinates\": [[[51.38, 35.6815], [51.3815, 35.6815], [51.3815, 35.683], [51.38, 35.683], [51.38, 35.6815]]]\n },\n \"properties\": {\"index\": 2}\n }\n ]\n },\n \"products\": [\"wheat\", \"canola\", \"saffron\"]\n}"},"url":"{{baseUrl}}/api/crop-zoning/zones/initial/","description":"Body: zones (FeatureCollection of grid squares), optional products. Returns initial data for map and hover/tooltip (no reason, criteria)."},"response":[{"name":"Success","status":"OK","code":200,"body":"{\n \"status\": \"success\",\n \"data\": {\n \"total_area_hectares\": 23.45,\n \"total_area_sqm\": 234500,\n \"zone_count\": 3,\n \"zones\": [\n {\n \"zoneId\": \"zone-0\",\n \"geometry\": {\"type\": \"Polygon\", \"coordinates\": [[[51.38, 35.68], [51.3815, 35.68], [51.3815, 35.6815], [51.38, 35.6815], [51.38, 35.68]]]},\n \"crop\": \"wheat\",\n \"matchPercent\": 85,\n \"waterNeed\": \"۴۵۰۰-۵۵۰۰ m³/ha\",\n \"estimatedProfit\": \"۱۵-۲۵ میلیون/هکتار\"\n },\n {\n \"zoneId\": \"zone-1\",\n \"geometry\": {\"type\": \"Polygon\", \"coordinates\": [[[51.3815, 35.68], [51.383, 35.68], [51.383, 35.6815], [51.3815, 35.6815], [51.3815, 35.68]]]},\n \"crop\": \"canola\",\n \"matchPercent\": 78,\n \"waterNeed\": \"۵۰۰۰-۶۰۰۰ m³/ha\",\n \"estimatedProfit\": \"۲۰-۳۵ میلیون/هکتار\"\n },\n {\n \"zoneId\": \"zone-2\",\n \"geometry\": {\"type\": \"Polygon\", \"coordinates\": [[[51.38, 35.6815], [51.3815, 35.6815], [51.3815, 35.683], [51.38, 35.683], [51.38, 35.6815]]]},\n \"crop\": \"saffron\",\n \"matchPercent\": 92,\n \"waterNeed\": \"۳۰۰۰-۴۰۰۰ m³/ha\",\n \"estimatedProfit\": \"۵۰-۱۵۰ میلیون/هکتار\"\n }\n ]\n }\n}"}]},{"name":"Zones water-need (POST)","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"body":{"mode":"raw","raw":"{\n \"zones\": {\n \"type\": \"FeatureCollection\",\n \"features\": [\n {\"type\": \"Feature\", \"geometry\": {\"type\": \"Polygon\", \"coordinates\": [[[51.38, 35.68], [51.3815, 35.68], [51.3815, 35.6815], [51.38, 35.6815], [51.38, 35.68]]]}, \"properties\": {\"index\": 0}},\n {\"type\": \"Feature\", \"geometry\": {\"type\": \"Polygon\", \"coordinates\": [[[51.3815, 35.68], [51.383, 35.68], [51.383, 35.6815], [51.3815, 35.6815], [51.3815, 35.68]]]}, \"properties\": {\"index\": 1}},\n {\"type\": \"Feature\", \"geometry\": {\"type\": \"Polygon\", \"coordinates\": [[[51.38, 35.6815], [51.3815, 35.6815], [51.3815, 35.683], [51.38, 35.683], [51.38, 35.6815]]]}, \"properties\": {\"index\": 2}}\n ]\n }\n}"},"url":"{{baseUrl}}/api/crop-zoning/zones/water-need/","description":"Returns water need per zone for water need layer (level, value, color)."},"response":[{"name":"Success","status":"OK","code":200,"body":"{\n \"status\": \"success\",\n \"data\": {\n \"zones\": [\n {\"zoneId\": \"zone-0\", \"geometry\": {\"type\": \"Polygon\", \"coordinates\": [[[51.38, 35.68], [51.3815, 35.68], [51.3815, 35.6815], [51.38, 35.6815], [51.38, 35.68]]]}, \"level\": \"medium\", \"value\": \"۴۵۰۰-۵۵۰۰ m³/ha\", \"color\": \"#0ea5e9\"},\n {\"zoneId\": \"zone-1\", \"geometry\": {\"type\": \"Polygon\", \"coordinates\": [[[51.3815, 35.68], [51.383, 35.68], [51.383, 35.6815], [51.3815, 35.6815], [51.3815, 35.68]]]}, \"level\": \"high\", \"value\": \"۵۰۰۰-۶۰۰۰ m³/ha\", \"color\": \"#0369a1\"},\n {\"zoneId\": \"zone-2\", \"geometry\": {\"type\": \"Polygon\", \"coordinates\": [[[51.38, 35.6815], [51.3815, 35.6815], [51.3815, 35.683], [51.38, 35.683], [51.38, 35.6815]]]}, \"level\": \"low\", \"value\": \"۳۰۰۰-۴۰۰۰ m³/ha\", \"color\": \"#7dd3fc\"}\n ]\n }\n}"}]},{"name":"Zones soil-quality (POST)","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"body":{"mode":"raw","raw":"{\n \"zones\": {\n \"type\": \"FeatureCollection\",\n \"features\": [\n {\"type\": \"Feature\", \"geometry\": {\"type\": \"Polygon\", \"coordinates\": [[[51.38, 35.68], [51.3815, 35.68], [51.3815, 35.6815], [51.38, 35.6815], [51.38, 35.68]]]}, \"properties\": {\"index\": 0}},\n {\"type\": \"Feature\", \"geometry\": {\"type\": \"Polygon\", \"coordinates\": [[[51.3815, 35.68], [51.383, 35.68], [51.383, 35.6815], [51.3815, 35.6815], [51.3815, 35.68]]]}, \"properties\": {\"index\": 1}},\n {\"type\": \"Feature\", \"geometry\": {\"type\": \"Polygon\", \"coordinates\": [[[51.38, 35.6815], [51.3815, 35.6815], [51.3815, 35.683], [51.38, 35.683], [51.38, 35.6815]]]}, \"properties\": {\"index\": 2}}\n ]\n }\n}"},"url":"{{baseUrl}}/api/crop-zoning/zones/soil-quality/","description":"Returns soil quality per zone for soil quality layer (level, score, color)."},"response":[{"name":"Success","status":"OK","code":200,"body":"{\n \"status\": \"success\",\n \"data\": {\n \"zones\": [\n {\"zoneId\": \"zone-0\", \"geometry\": {\"type\": \"Polygon\", \"coordinates\": [[[51.38, 35.68], [51.3815, 35.68], [51.3815, 35.6815], [51.38, 35.6815], [51.38, 35.68]]]}, \"level\": \"high\", \"score\": 88, \"color\": \"#22c55e\"},\n {\"zoneId\": \"zone-1\", \"geometry\": {\"type\": \"Polygon\", \"coordinates\": [[[51.3815, 35.68], [51.383, 35.68], [51.383, 35.6815], [51.3815, 35.6815], [51.3815, 35.68]]]}, \"level\": \"medium\", \"score\": 62, \"color\": \"#eab308\"},\n {\"zoneId\": \"zone-2\", \"geometry\": {\"type\": \"Polygon\", \"coordinates\": [[[51.38, 35.6815], [51.3815, 35.6815], [51.3815, 35.683], [51.38, 35.683], [51.38, 35.6815]]]}, \"level\": \"high\", \"score\": 95, \"color\": \"#22c55e\"}\n ]\n }\n}"}]},{"name":"Zones cultivation-risk (POST)","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"body":{"mode":"raw","raw":"{\n \"zones\": {\n \"type\": \"FeatureCollection\",\n \"features\": [\n {\"type\": \"Feature\", \"geometry\": {\"type\": \"Polygon\", \"coordinates\": [[[51.38, 35.68], [51.3815, 35.68], [51.3815, 35.6815], [51.38, 35.6815], [51.38, 35.68]]]}, \"properties\": {\"index\": 0}},\n {\"type\": \"Feature\", \"geometry\": {\"type\": \"Polygon\", \"coordinates\": [[[51.3815, 35.68], [51.383, 35.68], [51.383, 35.6815], [51.3815, 35.6815], [51.3815, 35.68]]]}, \"properties\": {\"index\": 1}},\n {\"type\": \"Feature\", \"geometry\": {\"type\": \"Polygon\", \"coordinates\": [[[51.38, 35.6815], [51.3815, 35.6815], [51.3815, 35.683], [51.38, 35.683], [51.38, 35.6815]]]}, \"properties\": {\"index\": 2}}\n ]\n }\n}"},"url":"{{baseUrl}}/api/crop-zoning/zones/cultivation-risk/","description":"Returns cultivation risk per zone for risk layer (level, color)."},"response":[{"name":"Success","status":"OK","code":200,"body":"{\n \"status\": \"success\",\n \"data\": {\n \"zones\": [\n {\"zoneId\": \"zone-0\", \"geometry\": {\"type\": \"Polygon\", \"coordinates\": [[[51.38, 35.68], [51.3815, 35.68], [51.3815, 35.6815], [51.38, 35.6815], [51.38, 35.68]]]}, \"level\": \"low\", \"color\": \"#22c55e\"},\n {\"zoneId\": \"zone-1\", \"geometry\": {\"type\": \"Polygon\", \"coordinates\": [[[51.3815, 35.68], [51.383, 35.68], [51.383, 35.6815], [51.3815, 35.6815], [51.3815, 35.68]]]}, \"level\": \"medium\", \"color\": \"#f59e0b\"},\n {\"zoneId\": \"zone-2\", \"geometry\": {\"type\": \"Polygon\", \"coordinates\": [[[51.38, 35.6815], [51.3815, 35.6815], [51.3815, 35.683], [51.38, 35.683], [51.38, 35.6815]]]}, \"level\": \"low\", \"color\": \"#22c55e\"}\n ]\n }\n}"}]},{"name":"Zone details (GET)","request":{"method":"GET","header":[{"key":"Content-Type","value":"application/json"}],"url":"{{baseUrl}}/api/crop-zoning/zones/zone-0/details/","description":"Returns detail data for one zone (reason, criteria, area_hectares) for detail panel and radar chart."},"response":[{"name":"Success","status":"OK","code":200,"body":"{\n \"status\": \"success\",\n \"data\": {\n \"zoneId\": \"zone-0\",\n \"crop\": \"wheat\",\n \"matchPercent\": 85,\n \"waterNeed\": \"۴۵۰۰-۵۵۰۰ m³/ha\",\n \"estimatedProfit\": \"۱۵-۲۵ میلیون/هکتار\",\n \"reason\": \"دمای مناسب، خاک حاصلخیز، دسترسی به آب کافی\",\n \"criteria\": [{\"name\": \"دما\", \"value\": 82}, {\"name\": \"بارش\", \"value\": 75}, {\"name\": \"خاک\", \"value\": 88}, {\"name\": \"آب\", \"value\": 70}],\n \"area_hectares\": 2.25\n }\n}"}]},{"name":"Zone details zone-2 (GET)","request":{"method":"GET","header":[{"key":"Content-Type","value":"application/json"}],"url":"{{baseUrl}}/api/crop-zoning/zones/zone-2/details/","description":"Returns detail data for zone-2 (saffron)."},"response":[{"name":"Success","status":"OK","code":200,"body":"{\n \"status\": \"success\",\n \"data\": {\n \"zoneId\": \"zone-2\",\n \"crop\": \"saffron\",\n \"matchPercent\": 92,\n \"waterNeed\": \"۳۰۰۰-۴۰۰۰ m³/ha\",\n \"estimatedProfit\": \"۵۰-۱۵۰ میلیون/هکتار\",\n \"reason\": \"ارتفاع و آب و هوای خشک مناسب، پتانسیل سود بالا\",\n \"criteria\": [{\"name\": \"دما\", \"value\": 90}, {\"name\": \"بارش\", \"value\": 65}, {\"name\": \"خاک\", \"value\": 95}, {\"name\": \"آب\", \"value\": 85}],\n \"area_hectares\": 2.25\n }\n}"}]}],"variable":[{"key":"baseUrl","value":"http://localhost:8000"}]} diff --git a/crop_zoning/urls.py b/crop_zoning/urls.py index 491a087..75983c5 100644 --- a/crop_zoning/urls.py +++ b/crop_zoning/urls.py @@ -1,8 +1,37 @@ from django.urls import path -from .views import InitialRegionView, OptimizeZoningView +from .views import ( + AreaView, + ProductsView, + ZoneDetailsView, + ZonesCultivationRiskView, + ZonesInitialView, + ZonesSoilQualityView, + ZonesWaterNeedView, +) urlpatterns = [ - path("optimize/", OptimizeZoningView.as_view(), name="crop-zoning-optimize"), - path("initial-region/", InitialRegionView.as_view(), name="crop-zoning-initial-region"), + path("area/", AreaView.as_view(), name="crop-zoning-area"), + path("products/", ProductsView.as_view(), name="crop-zoning-products"), + path("zones/initial/", ZonesInitialView.as_view(), name="crop-zoning-zones-initial"), + path( + "zones/water-need/", + ZonesWaterNeedView.as_view(), + name="crop-zoning-zones-water-need", + ), + path( + "zones/soil-quality/", + ZonesSoilQualityView.as_view(), + name="crop-zoning-zones-soil-quality", + ), + path( + "zones/cultivation-risk/", + ZonesCultivationRiskView.as_view(), + name="crop-zoning-zones-cultivation-risk", + ), + path( + "zones//details/", + ZoneDetailsView.as_view(), + name="crop-zoning-zone-details", + ), ] diff --git a/crop_zoning/views.py b/crop_zoning/views.py index 3023f2e..12b2d1f 100644 --- a/crop_zoning/views.py +++ b/crop_zoning/views.py @@ -3,65 +3,214 @@ Crop Zoning API views. Plain Django only; no DRF. No database. All responses are static mock data. Response format: {"status": "success", "data": }. HTTP 200 only. No processing, validation, or use of input parameters in responses. +CSRF exempt on POST so frontend can call without token. """ from django.http import JsonResponse +from django.utils.decorators import method_decorator from django.views import View +from django.views.decorators.csrf import csrf_exempt -from .mock_data import INITIAL_REGION_RESPONSE, OPTIMIZE_ZONING_RESPONSE +from .mock_data import ( + AREA_RESPONSE_DATA, + PRODUCTS_RESPONSE_DATA, + ZONE_DETAILS_BY_ID, + ZONES_CULTIVATION_RISK_RESPONSE_DATA, + ZONES_INITIAL_RESPONSE_DATA, + ZONES_SOIL_QUALITY_RESPONSE_DATA, + ZONES_WATER_NEED_RESPONSE_DATA, +) -class OptimizeZoningView(View): +class AreaView(View): """ - POST endpoint for zoning optimization. + GET endpoint for fixed land area (GeoJSON polygon). Purpose: - Returns a static GeoJSON FeatureCollection of zones with crop suggestions - (API_RESPONSE_SPEC §1). Used when the user selects a region on the map - and triggers "optimize zoning". No processing is performed on the request. + Returns static land area polygon for display on map. User cannot + draw or edit the region; it is loaded from backend. Input parameters: - - body (optional): JSON body; may contain a GeoJSON Feature with Polygon. - Data type: object. Location: body. Not read or validated; not used in response. + None. Response structure: - status: string, always "success". - - data: object, GeoJSON FeatureCollection with features containing - geometry (Polygon) and properties (zoneId, crop, matchPercent, waterNeed, - estimatedProfit, reason, criteria). - - No processing or validation is performed on inputs. - """ - - def post(self, request): - return JsonResponse( - {"status": "success", "data": OPTIMIZE_ZONING_RESPONSE}, - status=200, - ) - - -class InitialRegionView(View): - """ - GET endpoint for the initial map region. - - Purpose: - Returns a static GeoJSON Feature with Polygon defining the initial - map region (API_RESPONSE_SPEC §2). Optional; used when the initial - region is loaded from the server instead of a fixed client mock. - - Input parameters: - None. Query parameters, if sent, are not read or used. - - Response structure: - - status: string, always "success". - - data: object, GeoJSON Feature with geometry.type "Polygon" and - coordinates as [longitude, latitude]; first and last point equal. + - data: object with key "area" (GeoJSON Feature with Polygon geometry). No processing or validation is performed on inputs. """ def get(self, request): return JsonResponse( - {"status": "success", "data": INITIAL_REGION_RESPONSE}, + {"status": "success", "data": AREA_RESPONSE_DATA}, + status=200, + ) + + +class ProductsView(View): + """ + GET endpoint for list of crop products and colors. + + Purpose: + Returns static list of cultivable products with display color and + Persian label for the Crop Zoning page (Legend and zone detail panel). + Used when loading the crop-zoning page. + + Input parameters: + - locale: string, optional. Location: query. Language code (e.g. fa, en). + Not read or used in response. + + Response structure: + - status: string, always "success". + - data: object with key "products" (array of { id, label, color }). + + No processing or validation is performed on inputs. + """ + + def get(self, request): + return JsonResponse( + {"status": "success", "data": PRODUCTS_RESPONSE_DATA}, + status=200, + ) + + +@method_decorator(csrf_exempt, name="dispatch") +class ZonesInitialView(View): + """ + POST endpoint for initial zone data (map + hover/tooltip). + + Purpose: + Accepts zones (FeatureCollection of grid squares) and returns static + initial data per zone for map rendering and hover/tooltip display. + Does not include reason or criteria (those are in zone details). + + Input parameters (body, JSON): + - zones: GeoJSON FeatureCollection. Location: body. Grid square polygons. + - products: array of strings, optional. Location: body. Product IDs. + Not read or used in response. + + Response structure: + - status: string, always "success". + - data: object with total_area_hectares, total_area_sqm, zone_count, + zones (array of { zoneId, geometry, crop, matchPercent, waterNeed, + estimatedProfit }). + + No processing or validation is performed on inputs. Input values are + not used in the response. + """ + + def post(self, request): + return JsonResponse( + {"status": "success", "data": ZONES_INITIAL_RESPONSE_DATA}, + status=200, + ) + + +@method_decorator(csrf_exempt, name="dispatch") +class ZonesWaterNeedView(View): + """ + POST endpoint for water need per zone (water need layer). + + Purpose: + Accepts zones (FeatureCollection) and returns static water need + per zone for the water need map layer (level, value, color). + + Input parameters (body, JSON): + - zones: GeoJSON FeatureCollection. Location: body. + - products: array of strings, optional. Location: body. Not used. + + Response structure: + - status: string, always "success". + - data: object with zones (array of { zoneId, geometry, level, value, color }). + + No processing or validation is performed on inputs. + """ + + def post(self, request): + return JsonResponse( + {"status": "success", "data": ZONES_WATER_NEED_RESPONSE_DATA}, + status=200, + ) + + +@method_decorator(csrf_exempt, name="dispatch") +class ZonesSoilQualityView(View): + """ + POST endpoint for soil quality per zone (soil quality layer). + + Purpose: + Accepts zones (FeatureCollection) and returns static soil quality + per zone for the soil quality map layer (level, score, color). + + Input parameters (body, JSON): + - zones: GeoJSON FeatureCollection. Location: body. + - products: array of strings, optional. Location: body. Not used. + + Response structure: + - status: string, always "success". + - data: object with zones (array of { zoneId, geometry, level, score, color }). + + No processing or validation is performed on inputs. + """ + + def post(self, request): + return JsonResponse( + {"status": "success", "data": ZONES_SOIL_QUALITY_RESPONSE_DATA}, + status=200, + ) + + +@method_decorator(csrf_exempt, name="dispatch") +class ZonesCultivationRiskView(View): + """ + POST endpoint for cultivation risk per zone (cultivation risk layer). + + Purpose: + Accepts zones (FeatureCollection) and returns static cultivation + risk per zone for the risk map layer (level, color). + + Input parameters (body, JSON): + - zones: GeoJSON FeatureCollection. Location: body. + - products: array of strings, optional. Location: body. Not used. + + Response structure: + - status: string, always "success". + - data: object with zones (array of { zoneId, geometry, level, color }). + + No processing or validation is performed on inputs. + """ + + def post(self, request): + return JsonResponse( + {"status": "success", "data": ZONES_CULTIVATION_RISK_RESPONSE_DATA}, + status=200, + ) + + +class ZoneDetailsView(View): + """ + GET endpoint for zone detail data (detail panel after click). + + Purpose: + Returns static detail data for a single zone: reason, criteria, + area_hectares for the detail panel and radar chart. + + Input parameters: + - zoneId: string. Location: path. Zone identifier (e.g. zone-0). + Not read or used in response. + + Response structure: + - status: string, always "success". + - data: object with zoneId, crop, matchPercent, waterNeed, + estimatedProfit, reason, criteria (array), area_hectares. + + No processing or validation is performed on inputs. Input values are + not used in the response. + """ + + def get(self, request, zone_id): + data = ZONE_DETAILS_BY_ID.get(zone_id, ZONE_DETAILS_BY_ID["zone-0"]) + return JsonResponse( + {"status": "success", "data": data}, status=200, )