پروژه اولیه را از این لینک دانلود کنید.
ابتدا پروژهی اولیه را دانلود و از حالت فشرده خارج کنید.
سپس با اجرای دستور npm i
پکیج های مورد نیاز را نصب نمایید
با اجرای دستور node server/index.js
سرور را اجرا نمایید.
با اجرای دستور npm start
پروژه را اجرا نمایید
شکرستون شهری است به شدت رو به رشد به همین جهت مهیار تصمیم گرفته است که به این شهر نقل مکان کند که اندک سهمی در رشد این شهر نیز داشته باشد. اما در این میان خبری بسیار مسرت بخش به مهیار رسید که بازیگر افسانه ای نقش ژنرال کنوبی در سری فیلم های جنگ ستارگان قصد سفر و بازدید به شکرستون را کرده است برای همین مهیار تصمیم گرفت که شغلش را از از برنامه نویس به به برنامه ریز تورهای سیاحتی تغییر دهد. اما قبل از این تصمیم کلان مهیار متوجه مشکل زیرساخت تکنولوژیکی شدیدی در شکرستون شد که حتی گریبانگیر تیم اسنپ نیز شده بود.
اسنپ در این شهر برخلاف سایر شهرها هیچ نقشه ای از آن ندارد و مردم شکرستون صرفا با وارد کردن نام محله های مبدا و مقصد درخواست تاکسی میکنند. فلذا مهیار از بچه های عمران و شهرسازی دانشگاه صنعتی شکرستون درخواست کرد که در سریع ترین زمان ممکن نقشه ای از راه های سطح شهر تهیه کنند تا تاکسی اینترنتی خودش را به بازار شکرستون عرضه کند. در گام بعدی مهیار API شکرستون را بر اساس اطلاعات جمع آوری شده، طراحی کرده است که اطلاعات زیر را بر میگرداند:
نقاط ضروری برای رسم راه ها برحسب طول و عرض جغرافیایی (بر حسب درجه) که از طریق api/node قابل دسترس است.
مربع در برگیرنده شکرستون که شامل بیشترین و کمترین، طول و عرض جغرافیایی این شهر می باشد و از طریق api/bounds
قابل دسترسی است.
همچنین شیوه اتصال نقاط در api/way
قابل دریافت است که در قالب آرایه ای از شی های برگردانده می شود.
وسیله نقلی اصلیه شکرستون برای مقابله با اثرات زیست محیطی، زیباسازی شهر و آوردن خنده بر لب مردم شکرستون و جذب توریست های داخلی و خارجی، بر خلاف بسیاری از کلانشهرا که ماشین های لاکچری را ترجیح میدهند، حیوان دوست داشتنی خر می باشد، اما تیم اسنپ با خرها مشکلی اساسی داشت که البتی ناشی از عدم وجود نقشه نیز بود، آن هم این بود که در اکثر مواقع با وجود اینکه خرها مسافر را به مقصد میرساندند اما از هر مسیری که دلشان میخواست حرکت میکردند که نه تنها کوتاه ترین مسیر نبود بلکه طولانی ترین مسیر نیز بود. نکته جالب دیگر شکرستون این است که تمامی راه های موجود در این شهر دوطرفه هستند. به همین جهت مهیار تصمیم گرفته است نام استارتاپش را "خرنپ" بگذارد.
مهیار در ابتدا به اشتباه گمان میکرد پیاده سازی و اجرای الگوریتم های مسیریابی در سمت مرورگر کار درست و عقلانی است اما پس از صحبتهایی مفصل با علی آقا که از دانشجویان ممتاز دانشگاه صنعتی شکرستون می باشند، متوجه شد که کار اشتباهی است و تصمیم گرفت پیشنهاد علی آقا را مبنی بر اینکه مسیریابی در سمت سرور انجام بگیرد را پذیرفت. لکن برای پیدا کردن یک الگوریتم مسیریابی خوب تصمیم گرفت پیش یکی از اساتید پیشگام در حوزه گراف دانشگاه شکرستون یعنی جناب دکتر دیکسترا برود که متاسفانه ایشان به مسافرت رفته بود، به همین جهت او به الگوریتم BFS بسنده کرد و جهت استفاده از آن آدرس api/route
را به آن اختصاص داد که با فرستادن شناسه مبدا و مقصد در قالب متد POST
، کوتاه ترین مسیر (یا حداقل از مسیر خرها بسیار کوتاه تر) را در قالب آرایه ای شامل شناسه Node ها را بر میگرداند.
همچنین او از شما خواسته سیستم برای گذاشتن نشان (Marker) روی نقشه نیز برنامه را به نحوی طراحی کنید تا بتواند مبدا و مقاصد مورد نظر را به راحتی تعیین نماید که در جلوتر به صورت مفصل آن را توضیح خواهد داد.
از آنجایی که ژنرال کنوبی راس ساعت 6:05PM به شکرستون میرسد مهیار از شما می خواهد تا این زمان برنامه را تکیمل کنید. هرچند طبق گفته های مهیار اسنپ قول داده است که اگر این برنامه در راس زمان مقرر تکیمل بشود، استارتاپ خرنپ را با قیمت خوبی بخرد و شما را نیز به عنوان یک توسعه دهند قابل و توانا استخدام نماید.
ازآنجایی که مهیار درگیر تدارک دیدن خوش آمد گویی ژنرال کنوبی است در گام اول از شما خواسته است که اطلاعات دریافتی از API را در قالب یک نقشه گرافیکی با استفاده از کتابخانه react طراحی نمایید. (قابل به ذکر است که مهیار چند کامپوننت از قبل آماده شده برای راحت تر شدن استایل دهی پروژه و همچنین تسریع در روند توسعه آن تدارک دیده است که جزییات هریک را در قالب یک داکیومنت در اختیار شما می گذارد).
همچنین مهیار با توجه به ضیق وقت، سلیب را که از تیم لیدهای بسیار خوب در توسعه نرم افزار، دادن نقشه راه و روند طراحی نرم افزار است را استخدام کرده که نرم افزار نهایی علاوه بر دیپلوی شدن در وقت موعد، maintainable و قابل توسعه بیشتر در آینده نه چندان دور باشد و هم چنین روند توسعه درست و اصولی را برای شما مشخص نماید.
برای ارزیابی درست تمارین شما رعایت موارد زیر الزامی است:
utils/calcDistance.js
در این فایل یک تابع هم نام با فایل وجود دارد که وظیفه تکمیل آن نیز بر عهده شما می باشد.به عنوان ورودی دو نقطه را که هر نقطه شامل طول و عرض جغرافیایی هستند را دریافت می کند و فاصله آن ها را محاسبه کرده و بر میگرداند. برای محاسبه فواصل درون شهری با دقت نسبتا خوبی میتوان از فرمول هاورسین (Haversian) استفاده نمود، که بیان ریاضی این به شرح زیر است.
https://quera.ir/contest/assignments/36266/problems/125390
که R بیانگر شعاع زمین برحسب متر و d بیانگر فاصله بین دو نقطه برحسب متر می باشد.
نکته: دقت شود که atan2 مفهوم کاملا متفاوتی نسب به تانژات معکوس دارد( فی الواقع بیان میزان اختلاف درجه نقطه ای دلخواه بر روی صفحه با محور افقی می باشد)
utils/calcPositionCss.js
در این فایل یک تابع هم نام با فایل وجود دارد که وظیفه تکمیل آن نیز بر عهده شما می باشد.به عنوان ورودی node, bound را میگیرد و مکان node را برحسب درصد نسبت به یکی left/right در راستای محور افقی و top/bottom در راستای محور عمودی بیان میکند.(دقت کنید که شکل نهایی خروجی باید مطابق گیف باشد و دارای هیچ چرخش اضافه ای نباشد)
راهنمایی I : اگر بخواهیم مولفه های موقعیت جغرافییایی را در قالب دستگاه کارتزین تصور کنیم، عرض جغرافیایی (longitude) معادل x و طول جفرافیایی (latitude) معادل y می شود.
راهنمایی II : راه های زیادی برای این نگاشت وجود دارد که بر حسب فرمول های ریاضی پیچیده با در نظر گرفتن کره ای بودن زمین می باشد، اما چون وقت محدود است مهیار بهبود این امر را به آینده موکول کرده و از دوست ریاضیدانش جان فون نیومان درخواست کمک کرده است که در فاز بعدی بهبود این ضعف نیز بر طرف شود، اما علی الحساب جان فون نیومان پیشنهاد کرده است که برای تخمین مکان نقطه بر روی صفحه، این امر محاسبه شود که این نقطه چه کسری از اضلاع مستطیل در برگیرنده شکرستون می باشد، برای مثال:
api/index.js
در این فایل شما باید 4 تابع را به نحوی پیاده سازی کنید که اطلاعات هر مسیر را از سرور دریافت کند
getBounds
برای دریافت اطلاعات bounds شهر شکرستون که درخواستی از نوع GET به آدرس مورد نظر ارسال میکند. نمونه داده دریافتی به شرح زیر است:getNodes
: برای دریافت موقعیت های جغرافیایی شهر شکرستون که درخواستی از نوع GET به آدرس مورد نظر ارسال میکند. نمونه داده دریافتی به شرح زیر است:getWays
: برای دریافت اطلاعات مسیرهای شهر شکرستون که درخواستی از نوع GET به آدرس مورد نظر ارسال میکند. نمونه داده دریافتی به شرح زیر است:getPath
که با فرستادن شناسه(id) مبدا و مقصد در قالب یک درخواست از نوع POST اطلاعات مسیر را دریافت می کند.useAPI
هدف از این هوک استفاده از توابع ذکر شده و ذخیره مقادیر دریافتی در سه state مجزا با نام های nodes, ways, bounds می باشد، در نهایت این هوک تنها این مقادیر این سه state را بر میگرداند.
useMarker
هدف از طراحی این هوک به شرح زیر است:
گذاشتن نشان روی نقشه
محاسبه نزدیک ترین node به نشان گذاشته شده
نمایش دادن نشان ها بر روی صفحه
این هوک به عنوان ورودی یک reference و اطلاعات نقشه را دریافت می کند و دارای دو state تحت عنوانه:
marker
برای ذخیره مکان نشان ها می باشد
Cords
برای ذخیره نزدیک ترین node به نشان وارد شده
و به عنوان خروجی توابع و استیت زیر را بر میگرداند:
addMarker
که بعنوان ورودی شی event را دریافت کرده و یک marker, cords جدید اضافه می کند
deleteMarker
هیچ ورودی نمی گیرد و صرفا state ها را خالی میکند
renderMarker
نشان ها را با استفاده از کامپوننتMarker.jsx
در قالب یک لیست جهت نمایش بر میگرداند، کامپوننت مارکر به عنوان Attribute دو ورودی x, y را میگیرد که بیانگر موفعیت آن در صفحه متناسبش می باشد.
cords
که شامل نزدیک ترین Node به هر نشان است.
نکته: محدودیتی در تعداد نشان ها وجود ندارد.
useNavigator
این هوک به عنوان ورودی اطلاعات نقشه و اطلاعات جغرافیایی نشان ها را دریافت میکند و بعنوان خروجی تالعی تحت عنوان renderWays
را بر میگرداند که وظیفه اصلی آن نمایش راه هاست. سپس با استفاده از API
طراحی شده در بخش قبل و با استفاده از نشان های دریافتی مسیر متناسب گذرنده میان نشان ها را دریافت میکند. برای نمایش مسیر شما باید در صورتی که راه در در حال رسم بخشی از مسیر پیش بینی شده بود پراپرتی مناسب را به آن پاس دهید.
همچنین تابع دیگری تحت عنوان getDistance
نیز برمیگرداند که مسافت طی شده را بر حسب متر با استفاده از تابع calcDistance
محاسبه کرده و حاصل آن را بر میگرداند.
useMap
هدف از طراحی این هوک استفاده از سه هوک طراحی شده قبلی میباشد و به عنوان ورودی یک reference
دریافت میکند و توابع زیر را به عنوان خروجی بر میگرداند.
تابعی تحت عنوان renderMap
برای رندر کردن تمام المان ها
تابع اضافه کردن نشان بر روی نقشه
تابع حذف نشان های روی نقشه
<Distance />
هدف از طراحی این کامپوننت نشان دادن مسافت پیموده شده با دقت دو رقم اعشار، نشان ها میباشد که باید درون یک تگ span
به صورت زیر نشان داده شود:
در صورتی که فاصله قابل تبدیل به km بود باید به صورت km ای نشان داده شود (یعنی اگر بزرگتر از 1000 متر بود) در غیر این صورت به صورت همان متر نشان داده شود:
که مقدار مسافت پیموده شده برحسب متر در قالب prop ای تحت عنوان distance پاس داده میشود:
<MapWidget />
این کامپوننت دو پراپ تحت عنوان های deleteMarkers که یک تابع است که عمل پاک کردن مارکر ها را انجام میدهد و دیگری فاصله بر حسب متر یا همان distance می باشد.
حال شما باید این کامپوننت را به نحوی تکمیل کنید که پراپها به نحو درست به کامپوننت های <Distance />
و <ClearButton />
پاس داده شوند و هردوی آنها درون <Row> </Row>
قرار بگیرند
<Map />
در گام آخر در این کامپوننت شما باید با استفاده از هوک useMap
طراحی شده نقشه را در درون <Relative/>
با کلاس map
نمایش دهید و همچنین در صورتی که بر روی آن کلیک شد باید یک نشان به مجموعه نشان ها اضافه شود.
و یک کامپوننت دیگر یعنی همان <MapWidget />
با پراپ های مطلوب نمایش داده شود.
و همچنین هردوی این کامپوننتها باید درون تگ <div>
قرار بگیرند
<Marker/>
به عنوان پراپهای ورودی متغییرهای x و y را که بیانگر فاصله از سمت چپ و بالا می باشند دریافت میکند، و یک المان به صورت absolute به عنوان marker را نمایش میدهد
<Row/>
یک ردیف ایجاد می کنید و هیچ ورودی دریافت نمی کند
<ClearButton />
به عنوان ورودی یک تابع onClick میگیرد
<Relative/>
یک المان با استایل relative نمایش میدهد
<Way />
به عنوان ورودی شناسه src و dst را دریافت کرده و متغیر bool تحت عنوان isPath نیز دریافت میکند که بیانگر مسیر بودن یا نبودن می باشد
<Node />
به عنوان ورودی طول و عرض جفرافیایی را در قالب lat و lon دریافت کرده علاوه بر آن مربع در برگیرنده را تحت عنوان bounds دریافت کرده و همچنین شناسه موقعیت جغرافیایی را نیز دریافت میکند.