# جریان واقعی `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. هر بلوک کشاورز به چند زیر‌بلوک داده‌محور تقسیم می‌شود این دقیقاً همان منطق اصلی جدید سیستم است.