Files
Logic/Modules/Ai/docs/location_data_full_architecture.md
T
2026-05-11 03:27:21 +03:30

22 KiB

مستند کامل اپ 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) ساختار فایل‌های مهم اپ

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:

SUBDIVISION_CHUNK_SQM = int(os.environ.get("SUBDIVISION_CHUNK_SQM", "900"))

مقدار پیش‌فرض فعلی:

  • 900

معنا:

  • grid analysis با سلول‌های 30m x 30m

چون:

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ها را نگه می‌دارد.

نمونه:

{
  "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 است:

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

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

فقط از:

aggregate_spatial(geometries=feature_collection, reducer="mean")

استفاده می‌شود.

slope support

اگر backend slope() را پشتیبانی نکند:

  • slope_deg = null
  • و metadata می‌گوید slope_supported=False

normalized output

خروجی نهایی به این شکل است:

{
  "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 تبدیل شده است.