فاصله یابی بین دو مکان با کمک نقشه گوگل درMVC

دوشنبه 9 مرداد 1396

هم نقشه گوگل و هم نقشه Bing ابزارهای بسیار مفید و کاربردی هستند و API های هر دوی آنها در دسترس برنامه نویسان قرار گرفته است که این توانایی را به ما می دهد که آنچه نیاز داریم به آنها اضافه کنیم. ما مجبور نیستیم از همه بخش های API استفاده کنیم، گاهی اوقات یک تغییر کوچک باعث ایجاد تفاوت های زیادی می شود.

فاصله یابی بین دو مکان با کمک نقشه گوگل درMVC

پیش زمینه

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

برای مثال در شغل لوله کشی، حتما یک برنامه روزانه برای هر کارمند وجود دارد. ولی اگر یک شرکت خدمات رسانی بزرگ در یک شهر بزرگ را مد نظر قرار دهیم، در طول روز حتما چندین کار اورژانسی پیش می آید که این کارها باید خارج از برنامه روزانه و در سریعترین وقت ممکن انجام شود، در اینجا برنامه روزانه کارمندان دچار تغییراتی می شود و آن کارمند باید تصمیم بگیرد که چگونه این کارهای اورژانسی را با توجه به مسیر رفت و آمد و شلوغی خیابان ها،در برنامه روزانه اش بگنجاند. روشی که برای ارسال کارمندان به نقاط مختلف استفاده می شود triage system(سیستمی که میزان حساسیت و اورژانسی بودن را بررسی می کند) است که این سیستم سه سوال می پرسد:

-این وظیفه چقدر مهم یا اورژانسی است؟

-این کار جدید، به کدام کارمند نزدیک تر است و چقدر نزدیک است؟

-کمترین زمانی که یک کارمند نزدیک می تواند خودش را به مکان این کار جدید برساند؟

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

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

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

-به کمک یک برنامه تلفن همراه، به ردیابی کارمندان می پردازیم، این برنامه هر 15 دقیقه مکان تمام کارمندان را در دیتابیس مرکزی ثبت می کند.

-یک برنامه "برنامه ریز(scheduler)" یا "دفترچه خاطرات" که دارای تقویم کامل و جامعی باشد و به زبان MVC طراحی شده باشد، که بتواند اطلاعات قرارها(کارها) و کارمندانی که باید آن کارها را انجام بدهند، مدیریت کند.

-یک برنامه سمت client برای مدیریت مشتری ها، که اطلاعات مکانی مشتری ها را نگه دارد.

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

قبل از آنکه شروع به کدزنی کنیم، لازم به ذکر است که ما در مقاله های قبلی در مورد نحوه استفاده از Google Map API آموزش های لازم را داده ایم. برای مشاهده مقاله قبلی از لینک روبرو استفاده کنید. آموزش ساخت یک InfoWindow سفارشی برای Google Maps در ASP.net MVC

Google API Key

برای شروع این کار، شما باید یک Google API key برای خودتان بسازید. فرایند ثبت نام بسیار ساده و راحت است مگر اینکه شما قصد استفاده از این Google API key را در مقیاس های خیلی بزرگ داشته، که در آنصورت باید حساب خود را شارژ کنید، ولی در حالت عادی نیاز به ورژن پولی نیست و همان نسخه معمولی جوابگوی اکثر کسب و کارها است. در زمانی که این مقاله نوشته می شود، شما می توانید در نسخه معمولی، 25000 بار نقشه را در طول روز بارگزاری کنید.

در پروژه ایی که از سایت دانلود می کنید، حتما API key شخصی خودتان را قرار دهید.

دریافت یک API key کار بسیار ساده است، ابتدا وارد سایت Google Map API page شود و روی گزینه 'Get API کلیک کنید و نام پروژه را مشخص کرده در عرض چند ثانیه API key شما آماده می شود.

API Key را در محل مناسبی نگه دارید، به زودی از آن استفاده خواهیم کرد.

تنظیمات کلی

مثالی که برای این مقاله آماده شده است در چارچوب ASP.net MVC با .net v4.5  است که در ویژوال استودیو 2015 نوشته شده است. برای شروع ما یک web project می سازیم و در این مثال ما _Layout.cshtml را خالی می کنیم چرا که نمی خواهیم مانند هیچ کدام از توابع استاندارد  application loginرا در بالای برنامه ببینیم. پس قطعه کد زیر را در _Layout.cshtml قرار دهید.

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>@ViewBag.Title - My ASP.NET Application</title>
    @Styles.Render("~/Content/css")
    @Scripts.Render("~/bundles/modernizr")
</head>
<body>
    <div class="container body-content">
        @RenderBody()
        <hr />
        <footer>
            <p>&copy; @DateTime.Now.Year - My ASP.NET Application</p>
        </footer>
    </div>

    @Scripts.Render("~/bundles/jquery")
    @Scripts.Render("~/bundles/bootstrap")
    @RenderSection("scripts", required: false)
</body>
</html>

در داخل صفحه index.cshtml ما به کاربر اجازه می دهیم که به هر دو برنامه آماده شده دسترسی داشته باشه و اکشن متد های مربوطه به آنها را در کنترلر Home قرار می دهیم.

<div class="row">
    <div class="col-md-12">
        <h2><a href="demo1">Example 1</a> - simple distance between two points</h2>
        <p>
            This demonstrates getting and displaying the distance between two points
        </p>

      
    </div>
</div>

<div class="row">
    <div class="col-md-12">
        <h2><a href="demo2">Example 2</a> - distance between one origin and multiple target destinations</h2>
        <p>
            This demonstrates getting and displaying the distance between multiple points
        </p>
    </div>
</div>

public ActionResult Demo1()
{
    return View();
}

public ActionResult Demo2()
{
    return View();
}

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

برای درک بهتر اصول کار، نسخه دمو 1 را باز کنید. در اینجا ما تنظمات نقشه گوگل را انجام می دهیم و با استفاده از API مسیر و فاصله بین دو نقطه را در نقشه نمایش می دهیم.

اولین کاری که باید انجام دهیم، تنظیم HTML و layout برای صفحه است. صفحه دارای یک نقشه و دو input است که به کاربر اجازه می دهد مکان شروع را انتخاب کند و در input دیگر کاربر می تواند مکان مقصد را مشخص کند. برای راحتی بیشتر در تست برنامه، ما مقادیر پیشفرضی در این input ها قرار داده ایم.

<div class="row">
    <div class="col-md-12">
        <h2>Example 1 - simple distance between two points</h2>
        <p>
            This demonstrates getting and displaying the distance between two points
        </p>

        <div>
            <div>
                Travel From : <input id="travelfrom" type="text" name="name" value="Chichester, UK" />
                To : <input id="travelto" type="text" name="name" value="Goodwood aerodrome, UK" />
                <input type="button" value="Get Route" onclick="GetRoute()" />

            </div>
            <br />
            <div>
                <div id="dvDistance">
                </div>
            </div>

        </div>

        <div id="dvMap" style="min-height:500px"></div>

    </div>
</div>

از کنترل های مهم صفحه بالا دو input با نام های travelfrom، travelto و یک کلید است که تابع GetRoute() را فراخوانی می کند.

مابقی کد در بخش script قرار دارد. اینجا، جایی است که ما از API key که در مراحل قبل ساخته بودیم، استفاده خواهیم کرد.

@section scripts{

   <!--ENSURE YOU CHANGE TO YOUR OWN API KEY HERE !!! --> 
   <script src="https://maps.googleapis.com/maps/api/js?libraries=places&key=YOUR_KEY">
   </script>

...

اولین کاری که باید انجام دهیم، تعریف چند متغیر جاوا اسکریپتی است. این متغییر ها حاوی مقادیری است که برای فراخوانی Map API مورد استفاده قرار می گیرند.

var source, destination;
       var directionsDisplay;
       var directionsService = new google.maps.DirectionsService();

یک عنصر DIV با نام dvMap را قبلا در کدهای html قرار داده بودیم و اکنون به Map API اعلام می کنیم که از این عنصر برای نمایش نقشه استفاده کند. ما همچنین یک مختصات جغرافیای خاص را به عنوان مرکز نقشه تنظیم کرده ایم. در این مثال از چند اسکریپت برای تغییر نقشه نمایش داده شده بین دفتر اصلی، کارمند و محل مشتری استفاده شده است تا کار با این برنامه برای کاربر ساده تر شود.

// initialize the location of the map on Chichester in England (ref lat and lng)
var map = new google.maps.Map(document.getElementById('dvMap'), {
    center: { lat: 50.834697, lng: -0.773792 },
    zoom: 13,
    mapTypeId: 'roadmap'
});

map API یک api خیلی بزرگ و جامع است و از تعداد زیادی بخش کوچک طراحی شده است که موقع برنامه نویسی بسیار کمک می کنند. برای مثال یک API وجود دارد که شما می توانید input را به 'type ahead'، متصل کنید، تا با وارد کردن بخشی از نام مکان، گوگل به شما مکان های مرتبط را پیشنهاد می دهد و این کار باعث می شود که پیدا کردن یک مکان بسیار ساده تر شود. در قطعه کد بعدی ما متد Searchbox را به input های travelfrom و travelto متصل می کنیم تا از قابلیت type ahead استفاده کنند. همچنین مقداری کد وجود دارد برای آنکه به کاربر اجازه حرکت در نقشه به وسلیه clicking/dragging به کمک ماوس داده شود.

google.maps.event.addDomListener(window, 'load', function () {
    new google.maps.places.SearchBox(document.getElementById('travelfrom'));
    new google.maps.places.SearchBox(document.getElementById('travelto'));
    directionsDisplay = new google.maps.DirectionsRenderer({ 'draggable': true });
});

مثال 1 – کد اصلی

ما یک فانکشن اصلی با نام GetRoute() داریم. این فانکشن مقادیر input های travelfrom و travelto را دریافت می کند و این مقادیر با API ارسال می کند و سپس نتیجه دریافتی از API را به نقشه نمایش می دهد.

اولین کاری که باید انجام دهیم تنظیم پارامتر هایی است که آنها را باید به API ارسال کنیم. سپس متد 'directionsService.route را برای نمایش نتیجه روی نقشه، فراخوانی می کنیم ('directionsDisplay.setDirections').

directionsDisplay.setMap(map);

source = document.getElementById("travelfrom").value;
destination = document.getElementById("travelto").value;

var request = {
    origin: source,
    destination: destination,
    travelMode: google.maps.TravelMode.DRIVING
};

directionsService.route(request, function (response, status) {
    if (status == google.maps.DirectionsStatus.OK) {
        directionsDisplay.setDirections(response);
    }
});

نمایش نقشه به کاربر یک کار مهم است ولی برای کاری ما انجام می دهیم باید مجموعه اطلاعات خاصی به کاربر نمایش داده شود، مانند فاصله بین دو مکان و زمانی که لازم است تا از مبدا به مقصد رسید (در مثال ما همان زمان رسیدن کارمند به محل کار است). برای دست یابی به این اطلاعات ما باید متد 'Distance matrix service را فراخوانی کنیم.

مرحله اول اعمال تنظیمات است، باید مقادیر مبدا و مقصد و نوع رفت و آمد(مثلا پیاده یا با ماشین) را مشخص کرد همچنین باید واحد مقادیر سیستمی را وارد کرد مثلا در اینجا ما نوع رفت و آمد را DRIVING انتخاب کرده ایم و نوع واحد ها را METRIC انتخاب کرده ایم.

var service = new google.maps.DistanceMatrixService();
service.getDistanceMatrix({
    origins: [source],
    destinations: [destination],
    travelMode: google.maps.TravelMode.DRIVING,
    unitSystem: google.maps.UnitSystem.METRIC,
    avoidHighways: false,
    avoidTolls: false

در مرحله دوم باید به مدیریت جواب های دریافتی از API بپردازیم. مقادیر مسافت و زمان را دریافت کنیم و این مقادیر را به کمک یک عنصر div با نام dvDistance در صفحه به کاربر نمایش دهیم.

       }, function (response, status) {

                if (status == google.maps.DistanceMatrixStatus.OK && 
                              response.rows[0].elements[0].status != "ZERO_RESULTS") {
                    var distance = response.rows[0].elements[0].distance.text;
                    var duration = response.rows[0].elements[0].duration.value;
                    var dvDistance = document.getElementById("dvDistance");
                    duration = parseFloat(duration / 60).toFixed(2);
                    dvDistance.innerHTML = "";
                    dvDistance.innerHTML += "Distance: " + distance + "<br />";
                    dvDistance.innerHTML += "Time:" + duration + " min";

خروجی برنامه

پیدا کردن مسافت و مسیر بین چند مکان

برای اینکه به کاربر قابلیت انعطاف بیشتری بدهیم و به کاربر اجازه بدهیم که برای کارمندان ساده تر بتواند طرح زمان بندی روزانه را طراحی کند، باید از یک API با نام waypoint که در مجموعه api های Google Maps است، استفاده کنیم. تا به کاربر اجازه انتخاب چند مکان در نقشه را بدهیم و در آخر یک مسیر مناسب را به کاربر نمایش دهیم.

اولین کاری که ما انجام می دهیم تنظیم UI است، که کاربر بتواند مجموعه ایی از مقصد های احتمالی را وارد نماید. در این محصول نهایی باید این مقادیر به صورت اتوماتیک از داخل دیتابیس آورده شود و در این مکانها قرار داده شود، این اطلاعات در واقع مکان مشتری ها است که باید در دیتابیس نگه داری شود.

همانطور که مشاهده می کنید ما برای تست راحت تر برنامه، مقادیر پیش فرضی را در داخل برنامه مشخص کرده ایم.

در کدهای HTML داخل layout تفاوت هایی وجود دارد که در زیر به آنها اشاره کرده ایم:

1- ما یک آرایه جاوااسکریپتی از مقصد های احتمالی ساخته ایم که با استفاده از متد PushDestination() می توانیم به این آرایه مقدار جدیدی بیافزاییم.

2- استفاده از مقادیر پیشفرض باعث می شود که ما بتوانیم به سرعت برنامه را به کمک متد setDestination() تست کنیم.

3- عنصر div با نام destinations یک لیست از مقصدها را قبل از آنکه ما مسافت ها و مسیر ها را محساب کنیم، ذخیره می نماید.

4- در آخر، ما یک جدول داریم که به صورت زیبا و مرتب به نمایش، نتایجی که از API دریافت می کنیم، می پردازد.

<div> Add Destination</div>
        <div>
            <input id="travelto" type="text" name="name" value="Oving, UK" />
            <input type="button" value="Add" onclick="PushDestination()" />
            <a href="#" onclick="setDestination('Tagmere, UK')">Tagmere, UK. </a>
            <a href="#" onclick="setDestination('Bosham, UK')">Bosham, UK</a>
        </div>
        <div id="destinations"></div><br />
        Source : <input id="travelfrom" type="text" name="name" value="Chichester, UK" />   
           
        <input type="button" value="Calculate" onclick="GetRoute()" />
        <p></p>
        <br />
        <div id="dvDistance">
            <table id="tblResults" border="1" cellpadding="10">
                <tr>
                    <th> Start </th>
                    <th> End </th>
                    <th> Distance </th>
                    <th> Duration </th>
                </tr>
            </table>

        </div>

        <div id="dvMap" style="min-height:500px"></div>

از کدهای جاوا اسکریپتی که در مراحل قبل نوشته بودیم با همان تنطیمات قبلی استفاده می کنیم با این تفاوت که در این مثال باید آرایه locations را به آن اضافه کنیم.

var source, destination;
       var locations = [];
       var directionsDisplay;
       var directionsService = new google.maps.DirectionsService();

       // initialize the location of the map on Chichester in England (ref lat and lng)
       var map = new google.maps.Map(document.getElementById('dvMap'), {
           center: { lat: 50.834697, lng: -0.773792 },
           zoom: 13,
           mapTypeId: 'roadmap'
       });

       google.maps.event.addDomListener(window, 'load', function () {
           new google.maps.places.SearchBox(document.getElementById('travelfrom'));
           new google.maps.places.SearchBox(document.getElementById('travelto'));
           directionsDisplay = new google.maps.DirectionsRenderer({ 'draggable': true });
       });

آرایه ای که برای مقصدهای احتمالی ساخته ایم توسط متد PushDestination()مدیریت می شود. این متد هر مقداری که در داخل 'travelto' باشد را دریافت می کند و آن را به این آرایه اضافه می کند. همچنین این متد صفحه ایی که در حال نمایش را ویرایش می کند و مقادیر جدید را در عنصر destinations اضافه می کند.

function PushDestination() {
    destination = document.getElementById("travelto").value;
    locations.push(destination);
    document.getElementById("travelto").value = "";
    destinationArray = document.getElementById("destinations");
    destinationArray.innerHTML += destination + "<br />";
}

متد کمکی که ساخته ایم، آیتم ها را در input اضافه می کند و سپس متد PushDestination() را فراخوانی می کند تا همه مقادیر کنار یکدیگر قرار داشته باشند.

function setDestination(dest)
{
    document.getElementById('travelto').value = dest;
    PushDestination();
}

متد GetRoute همانند همان قبلی کار می کند.

function GetRoute() {

    directionsDisplay.setMap(map);

    source = document.getElementById("travelfrom").value;
    destination = document.getElementById("travelto").value;

سپس یک آرایه با نام waypoints می سازیم و آن را پر می کنیم.

var waypoints = [];
for (var i = 0; i < locations.length; i++) {
    var address = locations[i];
    if (address !== "") {
        waypoints.push({
            location: address,
            stopover: true
        });
    }
}

پارامترهای درخواستی را با توجه به آنکه بیش از یک مقصد وجود دارد کمی اصلاح می کنیم. در داخل آرایه waypoints اضافه می کنیم و به API ارسال می کنیم تا مقادیر داخل waypoints را بهینه سازی نماید.

       var request = {
                origin: source,
                destination: waypoints[0].location,
                waypoints: waypoints, //an array of waypoints
                optimizeWaypoints: true, //set to true if you want Google to determine the 
                                         // shortest route or false to use the order specified.
                travelMode: google.maps.DirectionsTravelMode.DRIVING
            };

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

directionsService.route(request, function (response, status) {
    if (status == google.maps.DirectionsStatus.OK) {
        var dvDistance = document.getElementById("dvDistance");
        var distance = 0;
        var minute = 0.00;
        response.routes[0].legs.forEach(function (item, index) {
            if (index < response.routes[0].legs.length - 1) {
                distance = distance + parseInt(item.distance.text);
                minute = parseFloat(minute) + parseFloat(item.duration.value / 60);

                tbl = document.getElementById("tblResults");
                var row = tbl.insertRow(1);
                var cell = row.insertCell(0);
                cell.innerText = source;
                var cell = row.insertCell(1);
                cell.innerText = item.end_address;
                var cell = row.insertCell(2);
                cell.innerText = distance;
                var cell = row.insertCell(3);
                cell.innerText = minute.toFixed(2) + " min";
            }
        });
        directionsDisplay.setDirections(response);
    }
    else {
        //handle error
    }
})

خروجی برنامه

آموزش asp.net mvc

فایل های ضمیمه

برنامه نویسان

نویسنده 3355 مقاله در برنامه نویسان

کاربرانی که از نویسنده این مقاله تشکر کرده اند

در صورتی که در رابطه با این مقاله سوالی دارید، در تاپیک های انجمن مطرح کنید