Files
Logic/Modules/Ai/location_data/LOCATION_DATA_FLOW.md
2026-05-11 03:27:21 +03:30

10 KiB
Raw Permalink Blame History

جریان واقعی location_data

این توضیح دقیقاً بر اساس منطق جدید نوشته شده:

  • اول مختصات گوشه‌های کل زمین گرفته می‌شود
  • بعد مختصات بلوک‌هایی که کشاورز خودش تعریف کرده گرفته می‌شود
  • هر بلوک جداگانه به grid های 30×30 تبدیل می‌شود
  • برای هر grid داده‌ی یک بازه زمانی از openEO گرفته می‌شود
  • میانگین همان بازه، وضعیت نهایی همان grid حساب می‌شود
  • بعد برای همان grid ها KMeans اجرا می‌شود
  • برای هر K مقدار SSE / Inertia ذخیره می‌شود
  • نمودار K - SSE رسم می‌شود
  • نقطه‌ای که افت شیب ناگهانی دارد به عنوان تعداد مناسب زیر‌بلوک‌ها انتخاب می‌شود
  • در نهایت هر بلوک کشاورز به چند زیر‌بلوک داده‌محور تقسیم می‌شود

1) ورودی مرحله اول

در مرحله اول این داده‌ها ثبت می‌شوند:

  • مختصات گوشه‌های کل زمین
  • مختصات بلوک‌هایی که کشاورز تعریف کرده
  • کد هر بلوک

فایل اصلی:

  • location_data/views.py
  • location_data/serializers.py
  • location_data/models.py

خروجی این مرحله:

  • یک SoilLocation برای زمین
  • یک block_layout که داخلش boundary هر بلوک هست
  • یک BlockSubdivision برای هر بلوک، فقط به عنوان تعریف مرز بلوک کشاورز

نکته مهم:

  • در این مرحله هیچ subdivision سنکرونی اجرا نمی‌شود
  • هیچ داده خاکی از adapter قدیمی گرفته نمی‌شود

2) هر بلوک کشاورز جداگانه grid می‌شود

فایل اصلی:

  • location_data/grid_analysis.py

اینجا چه اتفاقی می‌افتد:

  • boundary هر بلوک خوانده می‌شود
  • آن بلوک به cell های 30×30 متر تبدیل می‌شود
  • برای هر cell یک رکورد ساخته می‌شود

مدل ذخیره:

  • AnalysisGridCell

هر AnalysisGridCell این چیزها را نگه می‌دارد:

  • cell_code
  • block_code
  • geometry
  • centroid_lat
  • centroid_lon
  • chunk_size_sqm

یعنی از اینجا به بعد، کوچک‌ترین واحد تحلیل ما دیگر خود بلوک نیست؛ بلکه grid های 30×30 داخل هر بلوک هستند.


3) داده ماهواره‌ای هر grid از openEO گرفته می‌شود

فایل اصلی:

  • location_data/openeo_service.py

منطق این بخش شبیه همان چیزی است که گفتی:

  • برای هر بازه زمانی، cube هر سنجنده load می‌شود
  • روی زمان mean_time() زده می‌شود
  • بعد برای geometry هر grid از aggregate_spatial(..., reducer=\"mean\") استفاده می‌شود

یعنی:

  • داده خام چند روز یا یک ماهه می‌آید
  • میانگین همان بازه زمانی برای هر grid محاسبه می‌شود
  • همان مقدار میانگین، وضعیت نهایی آن grid در آن بازه است

metric هایی که الان گرفته می‌شوند:

  • ndvi
  • ndwi
  • lst_c
  • soil_vv
  • soil_vv_db
  • dem_m
  • slope_deg

نکته مهم:

  • این داده‌ها برای تمام grid های یک بلوک گرفته می‌شوند
  • نه فقط برای مرکز مزرعه
  • نه فقط برای geometry خام

4) داده هر grid داخل جدول ذخیره می‌شود

فایل اصلی:

  • location_data/tasks.py

مدل ذخیره:

  • AnalysisGridObservation

برای هر grid و هر بازه زمانی، این داده‌ها ذخیره می‌شوند:

  • ndvi
  • ndwi
  • lst_c
  • soil_vv
  • soil_vv_db
  • dem_m
  • slope_deg

پس هر grid یک بردار ویژگی واقعی دارد.

یعنی به زبان ساده:

  • هر خانه 30×30 فقط یک polygon نیست
  • یک وضعیت داده‌ای واقعی هم دارد

5) اینجا یادگیری بدون نظارت استفاده می‌شود

فایل اصلی:

  • location_data/data_driven_subdivision.py

اینجا از:

  • KMeans

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

این بخش unsupervised است چون:

  • هیچ label آماده‌ای نداریم
  • فقط می‌خواهیم grid هایی که از نظر رفتار ماهواره‌ای شبیه هم هستند در یک گروه قرار بگیرند

6) feature matrix دقیقاً از چه چیزی ساخته می‌شود؟

هر سطر:

  • یک AnalysisGridCell

هر ستون:

  • یکی از feature های ماهواره‌ای

feature های پیش‌فرض:

  • ndvi
  • ndwi
  • lst_c
  • soil_vv_db
  • dem_m
  • slope_deg

یعنی ورودی KMeans از observation های واقعی می‌آید، نه از مختصات هندسی.


7) داده ناقص چطور مدیریت می‌شود؟

قبل از اجرای KMeans:

  • اگر یک grid برای همه feature ها خالی باشد، حذف می‌شود
  • اگر فقط بعضی feature ها خالی باشند، مقداردهی می‌شود

روش فعلی:

  • median imputation

بعد از آن:

  • داده‌ها استاندارد می‌شوند

روش فعلی:

  • StandardScaler

این کار لازم است چون:

  • مقیاس ndvi با dem_m فرق دارد
  • مقیاس dem_m با lst_c فرق دارد

8) برای هر K مقدار SSE ذخیره می‌شود

فایل اصلی:

  • location_data/data_driven_subdivision.py
  • location_data/block_subdivision.py

در زمان انتخاب تعداد خوشه:

  • برای K = 1, 2, 3, ...
  • مدل اجرا می‌شود
  • مقدار SSE / Inertia ذخیره می‌شود

این داده داخل metadata نتیجه clustering ذخیره می‌شود.

پس ما برای هر بلوک این را داریم:

  • لیست K
  • مقدار SSE هر K

9) نمودار K - SSE رسم می‌شود

منطق رسم نمودار در سیستم وجود دارد و از همان منطق elbow استفاده می‌شود.

هدف نمودار:

  • ببینیم از چه جایی به بعد کم شدن SSE دیگر خیلی شدید نیست
  • یعنی شیب نمودار ناگهان کمتر می‌شود

همان نقطه:

  • تعداد مناسب زیر‌بلوک‌های آن بلوک است

به زبان ساده:

  • اگر شیب تا K=3 خیلی زیاد کم شود
  • ولی بعد از آن خیلی آرام شود
  • K=3 انتخاب مناسب است

10) هر بلوک کشاورز جداگانه خوشه‌بندی می‌شود

این خیلی مهم است:

  • کل مزرعه یکجا خوشه‌بندی نمی‌شود
  • هر بلوکی که کشاورز تعریف کرده جداگانه پردازش می‌شود

پس برای هر بلوک:

  1. grid های 30×30 ساخته می‌شوند
  2. داده ماهواره‌ای همان grid ها گرفته می‌شود
  3. observation ذخیره می‌شود
  4. KMeans فقط روی grid های همان بلوک اجرا می‌شود
  5. تعداد زیر‌بلوک‌های مناسب همان بلوک تعیین می‌شود

11) نتیجه subdivision جدید کجا ذخیره می‌شود؟

مدل اصلی نتیجه:

  • RemoteSensingSubdivisionResult

این مدل چیزهای اصلی را نگه می‌دارد:

  • block_code
  • cluster_count
  • selected_features
  • skipped_cell_codes
  • kmeans_params
  • inertia_curve
  • cluster_summaries

و برای هر grid هم assignment جدا ذخیره می‌شود در:

  • RemoteSensingClusterAssignment

یعنی برای هر grid مشخص است:

  • در کدام cluster قرار گرفته
  • raw feature هایش چه بوده
  • scaled feature هایش چه بوده

12) BlockSubdivision الان چه نقشی دارد؟

الان BlockSubdivision دیگر مدل اصلی خوشه‌بندی نیست.

نقشش این است که:

  • boundary بلوک کشاورز را نگه دارد
  • metadata بلوک را نگه دارد
  • به grid سازی و pipeline کمک کند

اما نتیجه اصلی data-driven subdivision در این دو مدل ذخیره می‌شود:

  • RemoteSensingSubdivisionResult
  • RemoteSensingClusterAssignment

13) اجرای async کجا انجام می‌شود؟

فایل اصلی:

  • location_data/tasks.py

این pipeline داخل Celery اجرا می‌شود.

مراحل run:

  1. run ساخته می‌شود
  2. grid های بلوک ساخته می‌شوند
  3. داده openEO گرفته می‌شود
  4. observation ها ذخیره می‌شوند
  5. feature matrix ساخته می‌شود
  6. KMeans اجرا می‌شود
  7. نتیجه نهایی ذخیره می‌شود

مدل status:

  • RemoteSensingRun

وضعیت‌هایی که track می‌شوند:

  • pending
  • running
  • failed
  • completed

14) چیزی که حذف شده

این بخش‌ها دیگر منبع اصلی داده نیستند و باید حذف‌شده در نظر گرفته شوند:

  • منطق قدیمی دریافت soil depth
  • adapter های خاک
  • وابستگی اصلی به SoilDepthData

منبع اصلی داده از این به بعد:

  • داده ماهواره‌ای هر grid

یعنی:

  • به جای جدول depth-based
  • جدول observation های ماهواره‌ای grid-based مرجع اصلی است

15) خلاصه خیلی کوتاه

جریان نهایی این است:

  1. گوشه‌های زمین و بلوک‌های کشاورز ثبت می‌شوند
  2. هر بلوک به grid های 30×30 تبدیل می‌شود
  3. برای هر grid داده‌ی ماهواره‌ای یک بازه زمانی از openEO گرفته می‌شود
  4. میانگین آن بازه، وضعیت همان grid می‌شود
  5. همه grid ها در جدول observation ذخیره می‌شوند
  6. برای هر بلوک، روی feature های grid ها KMeans اجرا می‌شود
  7. برای هر K مقدار SSE ذخیره می‌شود
  8. نمودار K - SSE ساخته می‌شود
  9. elbow point تعداد مناسب زیر‌بلوک‌ها را مشخص می‌کند
  10. هر بلوک کشاورز به چند زیر‌بلوک داده‌محور تقسیم می‌شود

این دقیقاً همان منطق اصلی جدید سیستم است.