همه اقامتگاه‌های ایران در ۹۰۰ پیکسل: چگونه بین کیفیت و تراکم در فضای محدود نقشه تعادل ایجاد کردیم؟

همه اقامتگاه‌های ایران در ۹۰۰ پیکسل: چگونه بین کیفیت و تراکم در فضای محدود نقشه تعادل ایجاد کردیم؟

۱. نقطه شروع قصه 

داستان ما از یک سوال ساده در تجربه کاربری جاباما شروع شد؛ سوالی که لیست اقامتگاه‌ها به‌تنهایی نمی‌توانست به آن پاسخ بدهد: «این اقامتگاه دقیقاً کجاست؟»

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

راه‌حل روی کاغذ ساده بود: نتایج را روی یک نقشه جامع نشان دهیم. اما خیلی زود با محدودیت فیزیکی صفحه روبه‌رو شدیم. در دیزاین سیستم ما، یک پین با نمایش قیمت و امتیاز اقامتگاه حدود ۱۱۵ در ۸۲ پیکسل جا می‌گیرد. حتی در یک چیدمان شبکه‌ای ایده‌آل، ویوپورت یک موبایل فقط ظرفیت تعداد محدودی از این پین‌ها را دارد؛ در حالی که یک جست‌وجو در شهری مثل تهران می‌تواند صدها و هزاران اقامتگاه قابل‌نمایش داشته باشد.

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

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

ما به سیستمی نیاز داشتیم که در آن «جغرافیا» و «کیفیت اقامتگاه» بر سر تصاحب پیکسل‌ها با هم مذاکره کنند.

۲. از رتبه‌بندی اقامتگاه ها تا تخصیص توجه

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

برای همین دو نوع نمایش مستقل برای اقامتگاه‌ها در نظر گرفتیم. پین کامل قیمت را مستقیماً نشان می‌داد و فضای بیشتری از صفحه می‌گرفت؛ مینی‌پین اطلاعات کمتری نمایش می‌داد، اما با اشغال فضای بسیار کمتر اجازه می‌داد حضور گزینه‌های بیشتری روی نقشه حفظ شود. وقتی تراکم از حدی بالاتر می‌رفت که حتی این ترکیب هم دیگر قابل‌نمایش نبود، وارد حالت خوشه‌بندی می‌شدیم.

این تغییر ساده، صورت مسئله را عوض کرد. دیگر فقط نمی‌پرسیدیم «کدام اقامتگاه روی نقشه باشد؟»؛ باید تصمیم می‌گرفتیم هر اقامتگاه چقدر از توجه محدود کاربر و فضای محدود صفحه را دریافت کند. رتبه‌بندی الویت گزینه ها را  مشخص می‌کرد و هندسه نقشه مشخص می‌کرد کدام ترکیب از این نمایش‌ها واقعاً می‌تواند بدون تداخل روی صفحه قرار بگیرد.

۳. چرا تصمیم‌گیری را به بک‌اند بردیم؟

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

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

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

این انتخاب یک هزینه مهم هم داشت: بک‌اند دیگر فقط با طول و عرض جغرافیایی سروکار نداشت. باید مختصات را به فضای واقعی صفحه تبدیل می‌کرد و می‌فهمید کدام عناصر واقعاً با یکدیگر تداخل دارند.

۴. تعداد نتایج معیار خوبی برای تراکم نبود.

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

اما تعداد اقامتگاه‌ها لزوماً چیزی درباره شلوغی واقعی نقشه نمی‌گفت. صد اقامتگاه می‌توانستند در سراسر یک شهر پخش شده باشند و بدون مشکل روی نقشه جا بگیرند؛ همان صد اقامتگاه اگر در چند خیابان کنار هم متمرکز می‌شدند، عملاً فضایی برای پین‌های کامل باقی نمی‌گذاشتند.

برای همین تراکم را مستقیماً در فضای پیکسلی اندازه گرفتیم: مختصات جغرافیایی اقامتگاه‌ها به مختصات صفحه تبدیل می‌شد و تداخل پین‌ها با یکدیگر معیار تصمیم‌گیری قرار می‌گرفت.

از اینجا به بعد، سؤال دیگر این نبود که «چند اقامتگاه داریم؟»؛ سؤال این بود: «اگر واقعاً آن‌ها را روی این صفحه بگذاریم، چه اتفاقی می‌افتد؟»

۵. رقابت برای فضای صفحه

اگر نقشه هنوز می‌توانست در حالت پین باقی بماند، برای هر اقامتگاه دو پیشنهاد می‌ساختیم: یک پین کامل و یک مینی‌پین.

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

قواعد برخورد هم یکسان نبود. پین کامل حریم بیشتری برای خودش نگه می‌داشت؛ مینی‌پین‌ها می‌توانستند فشرده‌تر کنار هم قرار بگیرند.

اما در مناطق متراکم یک مشکل تازه ظاهر شد: گاهی الگوریتم آن‌قدر به نفع استفاده مؤثر از فضا عمل می‌کرد که نقشه پر از مینی‌پین می‌شد و تقریباً هیچ پین کاملی باقی نمی‌ماند. از نظر هندسی خروجی درست بود؛ از نظر کاربردپذیری نه. تراکم دیده می‌شد، اما گزینه‌های شاخص و قیمت‌ها تقریباً ناپدید می‌شدند.

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

اینجا یکی از تفاوت‌های مهم میان «چیدمان درست» و «تجربه درست» روشن شد: استفاده بهتر از فضای صفحه، همیشه به معنای نقشه بهتر نیست.

۶. خوشه‌بندی را برعکس حل کردیم

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

شهود مسئله کمی شبیه «مسئله تولد» بود: وقتی نقاط بیشتری را داخل تعداد محدودی ناحیه می‌ریزیم، احتمال اینکه چند نقطه سر از یک ناحیه دربیاورند خیلی سریع بالا می‌رود.

برای همین مسئله را برعکس حل کردیم. به‌جای اینکه یک اندازه ثابت برای سلول‌ها تعیین کنیم و بعد ببینیم چند خوشه ساخته می‌شود، ابتدا مشخص می‌کردیم تقریباً چند خوشه می‌خواهیم روی صفحه باقی بماند و اندازه سلول‌ها را متناسب با تعداد اقامتگاه‌ها و ابعاد همان ویوپورت تنظیم می‌کردیم.

در نتیجه، مقیاس خوشه‌بندی بخشی از خود مسئله بود، نه یک ثابت سراسری در تنظیمات.

۷. وقتی زوم دیگر قرارداد قابل اعتمادی نبود

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

کتابخانه‌های مختلف نقشه الزاماً تعریف یکسانی از مقیاس نداشتند. تفاوت‌هایی از جنس ۱۲۸ در برابر ۲۵۶ پیکسل در اندازه پایه جهان باعث می‌شد یک عدد زوم یکسان، روی دو پیاده‌سازی مختلف الزاماً به یک مقیاس پیکسلی یکسان منجر نشود.

برای الگوریتم ما این اختلاف مهم بود؛ چون تصمیم‌های چیدمان بر اساس فاصله و اندازه در فضای پیکسلی گرفته می‌شد. یک اختلاف در مقیاس می‌توانست باعث شود بک‌اند دو پین را جدا از هم ببیند، در حالی که روی صفحه واقعاً روی هم می‌افتادند.

برای همین زوم اعلام‌شده از سمت کلاینت را منبع حقیقت قرار ندادیم. بک‌اند با استفاده از محدوده جغرافیایی واقعی نقشه و ابعاد ویوپورت، مقیاس مورد نیاز خودش را دوباره محاسبه می‌کرد.

درس این قسمت ساده بود: وقتی تصمیمی به هندسه صفحه وابسته است، بهتر است به چیزهایی تکیه کنیم که واقعاً قابل مشاهده‌اند؛ نه مفهومی مثل «زوم» که ممکن است در دو پیاده‌سازی معنای دقیقاً یکسانی نداشته باشد.

۸. دستاوردها و سنجه‌ها

  • رشد نرخ تبدیل قبل و بعد از انتشار نقشه:
    نسخه جدید نقشه، در مقایسه با نسخه قبلی موبایل که همان اقامتگاه‌های لیست را روی نقشه نمایش می‌داد، نرخ تبدیل بالاتری ثبت کرده است. همچنین در مقایسه با خط مبنای چند ماه پیش و پس از انتشار، عملکرد نقشه جدید حدود ۶۵٪ بهتر بوده است.
  • عملکرد نقشه در مقایسه با لیست:
    نقشه از نظر تبدیل کاربر به مشتری، عملکرد بهتری نسبت به لیست نشان می‌دهد. سشن‌هایی که در آن‌ها از نقشه استفاده شده، ۲۵٪ نرخ تبدیل بالاتری نسبت به سایر سشن‌ها داشته‌اند. همچنین سشن‌هایی که کاربر در آن‌ها بیش از یک جست‌وجو روی نقشه انجام داده، ۵۵٪ نرخ تبدیل بالاتری داشته‌اند. این یافته‌ها نشان می‌دهد که نقشه می‌تواند به یکی از مسیرهای مهم کشف اقامتگاه در جاباما تبدیل شود.
  • توزیع متوازن‌تر توجه در بازار:

یکی از دستاوردهای مهم نقشه، توزیع متوازن‌تر نمایش میان اقامتگاه‌هاست. در لیست‌های رتبه‌بندی‌شده، توجه معمولاً به سمت اقامتگاه‌های محبوب‌تر متمرکز می‌شود؛ اما در نقشه، جغرافیا تا حدی جایگزین رتبه‌بندی می‌شود.

ضریب جینی که معیاری برای سنجش تمرکز توزیع است، در نقشه ۰.۶۵۳ و در لیست ۰.۷۰۹ بوده است؛ یعنی تمرکز نمایش در نقشه بیش از ۷٪ کمتر است. همچنین اقامتگاه‌های قرار گرفته در ۵۰٪ کم‌نمایش‌تر، حدود ۲.۳ واحد درصد سهم مواجهه بیشتری گرفته‌اند و اقامتگاه‌های قرارگرفته در ۱۰٪ پرنمایش‌تر، حدود ۷ واحد درصد سهم نمایش کمتری داشته‌اند.

  • استفاده مکرر از نقشه در حال تبدیل شدن به رفتار واقعی کاربر است:
    هنوز بخش بزرگی از کاربران جاباما نقشه را امتحان نکرده‌اند و در نسخه وب، به‌طور هفتگی حدود ۵ تا ۱۰٪ از سشن‌ها از نقشه برای رسیدن به اقامتگاه موردنظر استفاده می‌کنند. با این حال، سهم کاربرانی که از نقشه استفاده می‌کنند ماه‌به‌ماه در حال افزایش است و نسبت به هفته‌های ابتدایی انتشار این قابلیت، تاکنون حدود ۲.۲ برابر رشد داشته است.

۹. درس‌آموخته‌ها

  • حذف، آخرین گزینه است.
    یکی از مهم‌ترین تصمیم‌ها این بود که نمایش اقامتگاه را صفر و یک نبینیم. وقتی فضای کافی برای یک پین کامل وجود نداشت، به‌جای حذف اقامتگاه، سطح نمایش آن را کاهش دادیم و از مینی‌پین استفاده کردیم. این تجربه برای ما یک اصل عمومی‌تر ساخت: در سیستم‌هایی که با محدودیت منابع مواجه‌اند، داشتن چند سطح کیفیت معمولاً انعطاف بیشتری از حذف کامل ایجاد می‌کند.
  • بهترین چیدمان هندسی الزاماً بهترین تجربه محصول نیست.
    الگوریتم می‌توانست با استفاده بیشتر از مینی‌پین‌ها تعداد بیشتری اقامتگاه را بدون برخورد روی صفحه قرار دهد؛ از نظر هندسی این نتیجه بهتر بود. اما وقتی قیمت‌ها و گزینه‌های شاخص تقریباً ناپدید می‌شدند، تجربه محصول ضعیف‌تر می‌شد. اینجا مشخص شد که «استفاده حداکثری از فضا» هدف نیست؛ هندسه فقط یکی از محدودیت‌های مسئله است و objective نهایی باید رفتار موردنظر محصول را هم منعکس کند.
  • قرارداد هندسی باید از واقعیت قابل مشاهده ساخته شود، نه از فرض‌های کلاینت.
    زوم و مقیاسی که کلاینت گزارش می‌کند لزوماً همان چیزی نیست که الگوریتم برای تصمیم‌گیری نیاز دارد. هرجا توانستیم، هندسه موردنیاز را از اطلاعات بنیادی‌تر، مثل محدوده جغرافیایی و ابعاد واقعی ویوپورت، دوباره محاسبه کردیم. در نتیجه، داده کلاینت برای ما بیشتر یک ورودی قابل اعتبارسنجی شد تا منبع نهایی حقیقت.
  • خروجی به تنهایی برای مشاهده پذیری سیستم کافی نیست؛ باید دلیل تصمیم را هم ثبت کرد.
    در نسخه‌های اولیه می‌توانستیم ببینیم الگوریتم چه چیدمانی ساخته، اما نمی‌توانستیم توضیح دهیم چرا. برای دیباگ کردن چنین سیستمی، فقط ثبت پین‌ها و خوشه‌های خروجی کافی نیست. باید بدانیم یک پین چرا رد شده، چرا به مینی‌پین تنزل پیدا کرده، چه برخوردی باعث آن تصمیم شده و چه شرطی سیستم را وارد حالت خوشه‌بندی کرده است. مشاهده‌پذیری واقعی یعنی ثبت منطق تصمیم، نه فقط نتیجه آن.
  • ساختن یک فیچر خوب و استفاده کاربر از آن، دو مسئله مستقل‌اند.
    بعد از انتشار مشخص شد که عملکرد خوب نقشه برای کاربرانی که از آن استفاده می‌کنند، به‌تنهایی کافی نیست. بخش بزرگی از کاربران اصلاً وارد این مسیر نمی‌شوند. از آن نقطه، کشف فیچر دیگر برای ما یک مسئله جانبی نبود؛ خودش یک مسئله محصولی با قیف، آزمایش و سنجه‌های مستقل بود.