GIS

WebGIS برای شهرداری: مدیریت فضای سبز با ۱۲۰,۰۰۰ نقطه داده

وقتی شهرداری منطقه ۴ تبریز با مشکل مواجه شد که هر سال بودجه قابل توجهی برای فضای سبز هزینه می‌کند اما دقیقاً نمی‌داند چند درخت دارد، کجا هستند و وضعیت سلامت‌شان چیست، وقت آن رسیده بود که یک راه‌حل مبتنی بر داده طراحی شود. این پروژه ماه‌ها طول کشید، درس‌های سختی آموختیم، و در پایان سیستمی ساختیم که امروز ۱۲۰,۰۰۰ نقطه داده فضای سبز را در یک پلتفرم WebGIS مدیریت می‌کند.

در این مقاله معماری فنی سیستم، چالش‌هایی که با آن‌ها روبرو شدیم و درس‌هایی که آموختیم را به تفصیل شرح می‌دهیم. هدف این است که اگر سازمان مشابهی بخواهد مسیر مشابهی طی کند، از اشتباهات ما پرهیز کند.

معماری سیستم

برای انتخاب پشته فناوری، سه معیار اصلی داشتیم: هزینه صفر برای لایسنس (نرم‌افزار آزاد)، توانایی مقیاس‌پذیری به ۵۰۰+ کاربر همزمان، و قابلیت اجرا روی سرورهای داخل کشور. در نهایت به این پشته رسیدیم:

  • PostGIS — پایگاه داده اصلی مکانی. PostgreSQL با افزونه PostGIS تمام عملیات مکانی را با کارایی بالا انجام می‌دهد.
  • GeoServer — سرویس‌دهنده لایه‌های نقشه. WMS و WFS استاندارد برای سرویس‌دهی به کلاینت‌ها.
  • OpenLayers — کتابخانه نقشه در مرورگر. در مقایسه با Leaflet، کنترل بیشتری روی رندرینگ و انتخاب لایه‌ها داشت.
  • Django REST Framework — API پشتیبان برای منطق تجاری، احراز هویت و گزارش‌گیری.
  • Celery + Redis — پردازش ناهمزمان برای وظایف سنگین مثل تولید گزارش‌های PDF مکانی.
چرا GeoServer به‌جای MapServer یا pg_tileserv؟ در این پروژه نیاز به SLD-based styling پویا داشتیم که GeoServer آن را به‌خوبی پشتیبانی می‌کند. برای پروژه‌های جدیدتر، pg_tileserv + MapLibre GL را بررسی کنید.

مدل داده

طراحی مدل داده مهم‌ترین تصمیم در این پروژه بود. ما سه جدول اصلی تعریف کردیم: درختان، پارک‌ها و سوابق نگهداری. رابطه بین آن‌ها از طریق کلیدهای خارجی استاندارد برقرار است، اما نکته کلیدی ستون‌های geometry هستند که با نوع داده Geography PostGIS تعریف شده‌اند.

-- جدول درختان با هندسه مکانی CREATE TABLE trees ( id SERIAL PRIMARY KEY, species VARCHAR(100) NOT NULL, height_m NUMERIC(5,2), diameter_cm NUMERIC(6,2), health_status VARCHAR(20) CHECK (health_status IN ('excellent','good','fair','critical')), planted_at DATE, geom GEOGRAPHY(POINT, 4326) NOT NULL ); -- ایندکس مکانی GiST CREATE INDEX idx_trees_geom ON trees USING GIST (geom); -- جستجوی درختان بحرانی نزدیک به معابر اصلی SELECT t.id, t.species, ST_Distance(t.geom, m.geom) AS dist_to_road FROM trees t JOIN roads m ON ST_DWithin(t.geom, m.geom, 50) WHERE t.health_status = 'critical' ORDER BY dist_to_road;

این کوئری درختان بحرانی را که در شعاع ۵۰ متری معابر قرار دارند (ریسک افتادن روی اتومبیل یا عابر) شناسایی می‌کند و بر اساس فاصله مرتب می‌کند تا اولویت‌بندی نگهداری ساده‌تر باشد. اجرای این کوئری روی ۱۲۰,۰۰۰ درخت با ایندکس GiST کمتر از ۸۰ میلی‌ثانیه طول می‌کشد.

بهینه‌سازی عملکرد

اولین نسخه سیستم با ۱۲۰,۰۰۰ نقطه روی نقشه، مرورگر را به زانو در می‌آورد. زمان بارگذاری اولیه ۴۵ ثانیه بود که کاملاً غیرقابل قبول است. سه تغییر اساسی وضعیت را تبدیل کرد:

  • Tile-based rendering: به‌جای ارسال تمام نقاط به مرورگر، GeoServer تایل‌های PNG رندر می‌کند. مرورگر فقط تایل‌های visible viewport را درخواست می‌کند.
  • Cluster visualization: در زوم‌های پایین، نقاط نزدیک به هم با OpenLayers cluster strategy گروه‌بندی و به‌صورت دایره شماره‌دار نمایش داده می‌شوند.
  • Materialized views: آمار تجمیعی (تعداد درختان per district) به‌صورت Materialized View ذخیره شد و هر ۶ ساعت یک‌بار refresh می‌شود.
معیار قبل از بهینه‌سازی بعد از بهینه‌سازی بهبود
زمان بارگذاری اولیه ۴۵ ثانیه ۱.۸ ثانیه ۲۵×
زمان spatial query ۸.۲ ثانیه ۸۰ میلی‌ثانیه ۱۰۰×
حافظه مرورگر ۱.۲ گیگابایت ۸۵ مگابایت ۱۴×
کاربران همزمان ۵ نفر ۲۰۰+ نفر ۴۰×

چالش‌ها و درس‌ها

جدی‌ترین چالش پروژه کیفیت داده‌های اولیه بود. داده‌های درختان از منابع مختلف جمع‌آوری شده بود: سرشماری قدیمی سال ۱۳۹۵ روی کاغذ، فایل‌های Excel متناقض از واحدهای مختلف و داده‌های GPS جمع‌آوری‌شده با دستگاه‌های دقیق پایین. در برخی نواحی، ۳۰٪ مختصات اشتباه بودند یا در خیابان افتاده بودند به‌جای داخل پارک.

برای حل این مشکل، یک اپلیکیشن موبایل ساده با PWA (Progressive Web App) طراحی کردیم که تیم‌های میدانی شهرداری می‌توانستند با تلفن همراه معمولی، موقعیت دقیق درختان را ثبت و عکس بگیرند. این جمع‌آوری داده میدانی ۸ ماه طول کشید اما کیفیت داده را از ۷۰٪ به ۹۶٪ رساند.

درس دیگر این بود که رابط کاربری برای کارمند شهرداری باید با رابط کاربری برای مدیر ارشد متفاوت باشد. کارمند میدانی به نقشه ساده با دکمه‌های بزرگ نیاز دارد؛ مدیر به داشبورد تحلیلی با نمودار و آمار. ما اشتباه کردیم و اول یک رابط واحد ساختیم که برای هیچ‌کدام کافی نبود.

نکات کلیدی
  • ایندکس GiST در PostGIS کوئری‌های مکانی را تا ۱۰۰ برابر سریع‌تر می‌کند — هرگز بدون آن کار نکنید.
  • Tile-based rendering به‌جای ارسال vector data خام، تنها راه مقیاس‌پذیر برای صدهاهزار نقطه است.
  • کیفیت داده از معماری سیستم مهم‌تر است؛ بهترین GIS با داده غلط شکست می‌خورد.
  • جمع‌آوری داده موبایل با PWA، راه‌حل عملی برای سازمان‌هایی است که بودجه خرید GPS حرفه‌ای ندارند.