البرنامج التعليمي: استخدم Azure Maps لإنشاء محدد موقع المتجر

يرشدك هذا البرنامج التعليمي خلال عملية إنشاء محدد موقع متجر بسيط باستخدام Azure Maps.

ستتعلم في هذه البرنامج التعليمي كيفية:

  • قم بإنشاء صفحة ويب جديدة باستخدام Azure Map Control API.
  • قم بتحميل بيانات مخصصة من ملف وعرضها على الخريطة.
  • استخدم خدمة البحث في خرائط Azure للعثور على عنوان أو إدخال استعلام.
  • احصل على موقع المستخدم من المتصفح واعرضه على الخريطة.
  • اجمع طبقات متعددة لإنشاء رموز مخصصة على الخريطة.
  • نقاط بيانات المجموعة.
  • إضافة عناصر تحكم التكبير/التصغير إلى الخريطة.

المتطلبات الأساسية

إشعار

ولمزيد من المعلومات حول مصادقة Azure Maps، راجع إدارة المصادقة في Azure Maps.

التعليمة البرمجية العينة

يوضح هذا البرنامج التعليمي كيفية إنشاء محدد موقع متجر لشركة خيالية تسمى Contoso Coffee، بالإضافة إلى نصائح لتوسيع محدد موقع المتجر بوظائف إضافية.

لمشاهدة عينة مباشرة لما تقوم بإنشاءه في هذا البرنامج التعليمي، راجع Simple Store Locator على موقع نماذج التعليمات البرمجية خرائط Azure.

لمتابعة هذا البرنامج التعليمي وإشراكه بسهولة أكبر، قم بتنزيل الموارد التالية:

ميزات محدد موقع المتجر

يسرد هذا القسم ميزات خرائط Azure الموضحة في تطبيق محدد مواقع متجر Contoso Coffee الذي أنشئ في هذا البرنامج التعليمي.

ميزات واجهة المستخدم

  • يوجد شعار المتجر على العنوان
  • تدعم الخريطة التحريك والتكبير
  • زر My Location للبحث في الموقع الحالي للمستخدم.
  • تخطيط الصفحة الذي يضبط بناءً على عرض شاشة الأجهزة
  • مربع بحث وزر بحث

ميزات الوظائف

  • يؤدي keypress الحدث الذي تمت إضافته إلى مربع البحث إلى تشغيل بحث عندما يضغط المستخدم على مفتاح الإدخال Enter.
  • عندما تتحرك الخريطة، يعاد حساب المسافة إلى كل موقع من مركز الخريطة. يتم تحديث قائمة النتائج لعرض أقرب المواقع في أعلى الخريطة.
  • عندما يحدد المستخدم نتيجة في قائمة النتائج، يتم توسيط الخريطة فوق الموقع المحدد وتظهر معلومات حول الموقع في نافذة منبثقة.
  • عندما يحدد المستخدم موقعًا معيًنا، تقوم الخريطة بتشغيل نافذة منبثقة.
  • عندما يقوم المستخدم بالتصغير، يتم تجميع المواقع في مجموعات. يتم تمثيل كل مجموعة من خلال دائرة مع رقم داخل الدائرة. تتشكل المجموعات وتنفصل عندما يغير المستخدم مستوى التكبير / التصغير.
  • يؤدي تحديد مجموعة إلى تكبير مستويين على الخريطة والتركيز على موقع المجموعة.

تصميم محدد موقع المتجر

تُظهر لقطة الشاشة الموضحة أدناه التخطيط العام لتطبيق محدد مواقع متجر Contoso Coffee. لعرض النموذج المباشر والتفاعل معه، راجع تطبيق نموذج Simple Store Locator على موقع Azure Maps Code Samples.

لقطة شاشة تعرض محدد موقع متجر Contoso Coffee خرائط Azure نموذج التطبيق.

ولتحقيق أقصى استفادة من محدد موقع المتجر هذا، نقوم بتضمين تخطيط سريع الاستجابة يتم ضبطه عندما يكون عرض شاشة المستخدم أصغر من 700 بكسل. يُسهل التخطيط سريع الاستجابة استخدام محدد موقع المتجر على شاشة صغيرة، كما هو الحال على جهاز محمول. إليك لقطة شاشة تعرض عينة من تخطيط الشاشة الصغيرة:

لقطة شاشة توضح كيف يبدو تطبيق محدد مواقع متجر Contoso Coffee على جهاز محمول.

إنشاء مجموعة بيانات موقع المتجر

يصف هذا القسم كيفية إنشاء مجموعة بيانات للمتاجر التي تريد عرضها على الخريطة. يتم إنشاء مجموعة البيانات الخاصة بتحديد موقع Contoso Coffee داخل مصنف Excel. تحتوي مجموعة البيانات على 10213 موقعًا لمقاهي Contoso Coffee منتشرة عبر تسع دول أو مناطق: الولايات المتحدة وكندا والمملكة المتحدة وفرنسا وألمانيا وإيطاليا وهولندا والدنمارك وإسبانيا. فيما يلي لقطة شاشة لما تبدو عليه البيانات:

لقطة شاشة لبيانات محدد موقع المتجر في مصنف Excel.

قم بتنزيل ملف excel الذي يحتوي على مجموعة البيانات الكاملة لتطبيق عينة محدد موقع Contoso Coffee من مجلد البيانات لمستودع نماذج التعليمات البرمجية خرائط Azure في GitHub.

من لقطة الشاشة الموضحة أعلاه للبيانات، يمكننا تقديم الملاحظات التالية:

  • تخزن معلومات الموقع باستخدام AddressLine، City، Municipality (البلد)، AdminDivision (الولاية/المقاطعة)، PostCode (الرمز البريدي)، وCountry.
  • تحتوي أعمدة خط الطول وخطالعرض على إحداثيات لكل موقع من مواقع Contoso Coffee. إذا لم يكن لديك معلومات إحداثيات، يمكنك استخدام خدمة البحث لتحديد إحداثيات الموقع.
  • تحتوي بعض الأعمدة الأخرى على بيانات وصفية مرتبطة بالمقاهي: رقم هاتف وأعمدة منطقية وأوقات فتح وإغلاق المتجر بتنسيق 24 ساعة. الأعمدة المنطقية مخصصة لشبكة Wi-Fi وإمكانية الوصول إلى الكراسي المتحركة. يمكنك إنشاء أعمدة خاصة بك تحتوي على بيانات تعريف أكثر ملاءمة لبيانات موقعك.

إشعار

تعرض خرائط Azure بيانات إسقاط Spherical Mercator «EPSG:3857» «ولكنه يقرأ البيانات EPSG:4326» التي تستخدم WGS84 datum.

تحميل مجموعة بيانات الخاص بمحدد مواقع Contoso Coffee Shop

مجموعة بيانات محدد موقع Contoso Coffee shop صغيرة، بحيث يمكن تحويلها إلى ملف نصي محدد بعلامات جدولة يقوم المستعرض بتنزيله عند تحميل التطبيق.

تلميح

إذا كانت مجموعة البيانات الخاصة بك كبيرة للغاية بحيث يتعذّر على العميل تنزيلها، أو تم تحديثها بشكل متكرر، فقد تفكر في تخزين مجموعة البيانات الخاصة بك في قاعدة بيانات. بعد تحميل البيانات الخاصة بك في قاعدة بيانات، يمكنك بعد ذلك إعداد خدمة ويب تقبل الاستعلامات عن البيانات، ثم إرسال النتائج إلى متصفح المستخدم.

تحويل البيانات إلى ملف نصي محدد بعلامات التبويب

لتحويل بيانات موقع Contoso Coffee shop من مصنف Excel إلى ملف نصي محدد بعلامات جدولة:

  1. نزل مصنف Excel ContosoCoffee.xlsx وافتحه في Excel.

  2. حدد File > Save ِA....

  3. في القائمة المنسدلة حفظ بنوع ، حدد نص (جدولة محددة)(*.txt).

  4. قم بتسمية الملف ContosoCoffee.

لقطة شاشة لمربع الحوار حفظ بنوع.

إذا قمت بفتح الملف النصي في Notepad، فإنه يبدو مشابهًا للنص التالي:

لقطة شاشة لملف Notepad يعرض مجموعة بيانات محددة بعلامات جدولة.

إعداد المشروع

  1. افتح Visual Studio Code، أو الاختيارات الخاصة بك لبيئات التطوير.

  2. حدد File > Open Workspace....

  3. أنشئ مجلدًا جديدًا باسم ContosoCoffee.

  4. تأكد من تحديد CONTOSOCOFFEE في المستكشف.

  5. إنشاء الملفات الثلاثة التالية التي تعرف التخطيط والنمط والمنطق للتطبيق:

    • Index.html
    • index.css
    • index.js
  6. إنشاء مجلد باسم البيانات.

  7. أضف ملف ContosoCoffee.txt الذي أنشأته مسبقًا من مصنف Excel ContosoCoffee.xlsx إلى مجلد البيانات.

  8. إنشاء مجلد آخر باسم images.

  9. في حالة عدم قيامك بذلك بالفعل، فنزل 10 Map images من دليل الصور في مستودع GitHub وأضفها إلى مجلد الصور.

    يجب أن يبدو مجلد مساحة العمل الآن مثل لقطة الشاشة التالية:

    لقطة شاشة لمجلد الصور في دليل Contoso Coffee.

أنشئ HTML

لإنشاء HTML:

  1. أضف العلامات التالية meta إلى head index.html:

    <meta charset="utf-8">
    <meta http-equiv="x-ua-compatible" content="IE=Edge">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    
  2. أضف مراجع إلى ملفات JavaScript وCSS للتحكم في Azure Maps على الويب:

    <!-- Add references to the Azure Maps Map control JavaScript and CSS files. -->
    <link rel="stylesheet" href="https://atlas.microsoft.com/sdk/javascript/mapcontrol/3/atlas.min.css" type="text/css">
    <script src="https://atlas.microsoft.com/sdk/javascript/mapcontrol/3/atlas.min.js"></script>
    
  3. إضافة مراجع إلى index.js index.css.

    <!-- Add references to the store locator JavaScript and CSS files. -->
    <link rel="stylesheet" href="index.css" type="text/css">
    <script src="index.js"></script>
    
  4. في النص الأساسي للمستند، أضف header علامة. داخل العلامة header ، أضف الشعار واسم الشركة.

    <header>
        <img src="images/Logo.png" />
        <span>Contoso Coffee</span>
    </header>
    
  5. أضف علامة main وأنشئ لوحة بحث تحتوي على مربع نص وزر بحث. div أضف أيضا مراجع للخريطة ولوحة القائمة وزر My Location GPS.

    <main>
        <div class="searchPanel">
            <div>
                <input id="searchTbx" type="search" placeholder="Find a store" />
                <button id="searchBtn" title="Search"></button>
            </div>
        </div>
        <div id="listPanel"></div>
        <div id="myMap"></div>
        <button id="myLocationBtn" title="My Location"></button>
    </main>
    

بمجرد الانتهاء، يجب أن يبدو index.html مثل Locator.html Simple Store في نموذج التعليمات البرمجية التعليمية.

حدد أنماط CSS

الخطوة التالية هي تحديد أنماط CSS. تحدد أنماط CSS كيفية تخطيط مكونات التطبيق ومظهر التطبيق.

  1. افتح index.css.

  2. أضف التعليمة البرمجية css التالية:

    إشعار

    @media يحدد النمط خيارات الأنماط البديلة لاستخدامها عندما يكون عرض الشاشة أصغر من 700 بكسل.

     html, body {
         padding: 0;
         margin: 0;
         font-family: Gotham, Helvetica, sans-serif;
         overflow-x: hidden;
     } 
    
     header {
         width: calc(100vw - 10px);
         height: 30px;
         padding: 15px 0 20px 20px;
         font-size: 25px;
         font-style: italic;
         font-family: "Comic Sans MS", cursive, sans-serif;
         line-height: 30px;
         font-weight: bold;
         color: white;
         background-color: #007faa;
     }
    
     header span {
         vertical-align: middle;
     }
    
     header img {
         height: 30px;
         vertical-align: middle;
     }
    
     .searchPanel {
         position: relative;
         width: 350px;
     }
    
     .searchPanel div {
         padding: 20px;
     }
    
     .searchPanel input {
         width: calc(100% - 50px);
         font-size: 16px;
         border: 0;
         border-bottom: 1px solid #ccc;
     }
    
     #listPanel {
         position: absolute;
         top: 135px;
         left: 0px;
         width: 350px;
         height: calc(100vh - 135px);
         overflow-y: auto;
     }
    
     #myMap { 
         position: absolute;
         top: 65px;
         left: 350px;
         width: calc(100vw - 350px);
         height: calc(100vh - 65px);
     }
    
     .statusMessage {
         margin: 10px;
     }
    
     #myLocationBtn, #searchBtn {
         margin: 0;
         padding: 0;
         border: none;
         border-collapse: collapse;
         width: 32px;
         height: 32px; 
         text-align: center;
         cursor: pointer;
         line-height: 32px;
         background-repeat: no-repeat;
         background-size: 20px;
         background-position: center center;
         z-index: 200;
     }
    
     #myLocationBtn {
         position: absolute;
         top: 150px;
         right: 10px;
         box-shadow: 0px 0px 4px rgba(0,0,0,0.16);
         background-color: white;
         background-image: url("images/GpsIcon.png");
     }
    
     #myLocationBtn:hover {
         background-image: url("images/GpsIcon-hover.png");
     }
    
     #searchBtn {
         background-color: transparent;
         background-image: url("images/SearchIcon.png");
     }
    
     #searchBtn:hover {
         background-image: url("images/SearchIcon-hover.png");
     }
    
     .listItem {
         height: 50px;
         padding: 20px;
         font-size: 14px;
     }
    
     .listItem:hover {
         cursor: pointer;
         background-color: #f1f1f1;
     }
    
     .listItem-title {
         color: #007faa;
         font-weight: bold;
     }
    
     .storePopup {
         min-width: 150px;
     }
    
     .storePopup .popupTitle {
         border-top-left-radius: 4px;
         border-top-right-radius: 4px;
         padding: 8px;
         height: 30px;
         background-color: #007faa;
         color: white;
         font-weight: bold;
     }
    
     .storePopup .popupSubTitle {
         font-size: 10px;
         line-height: 12px;
     }
    
     .storePopup .popupContent {
         font-size: 11px;
         line-height: 18px;
         padding: 8px;
     }
    
     .storePopup img {
         vertical-align:middle;
         height: 12px;
         margin-right: 5px;
     }
    
     /* Adjust the layout of the page when the screen width is fewer than 700 pixels. */
     @media screen and (max-width: 700px) {
         .searchPanel {
             width: 100vw;
         }
    
         #listPanel {
             top: 385px;
             width: 100%;
             height: calc(100vh - 385px);
         }
    
         #myMap {
             width: 100vw;
             height: 250px;
             top: 135px;
             left: 0px;
         }
    
         #myLocationBtn {
             top: 220px;
         }
     }
    
     .mapCenterIcon {
         display: block;
         width: 10px;
         height: 10px;
         border-radius: 50%;
         background: orange;
         border: 2px solid white;
         cursor: pointer;
         box-shadow: 0 0 0 rgba(0, 204, 255, 0.4);
         animation: pulse 3s infinite;
     }
    
     @keyframes pulse {
         0% {
             box-shadow: 0 0 0 0 rgba(0, 204, 255, 0.4);
         }
    
         70% {
             box-shadow: 0 0 0 50px rgba(0, 204, 255, 0);
         }
    
         100% {
             box-shadow: 0 0 0 0 rgba(0, 204, 255, 0);
         }
     }
    

إذا قمت بتشغيل التطبيق في هذه المرحلة، يظهر الرأس ومربع البحث وزر البحث. ومع ذلك، فإن الخريطة غير مرئية لأنه لم يتم تحميلها بعد. إذا حاولت إجراء بحث، فلن يحدث شيء. يصف القسم التالي إضافة منطق JavaScript المطلوب للوصول إلى جميع وظائف محدد موقع المتجر.

إضافة التعليمة البرمجية لـ JavaScript

تمكّن شفرة JavaScript في تطبيق Contoso Coffee shop Locator العمليات التالية:

  1. إضافة مستمع حدث يسمى ready للانتظار حتى تكتمل الصفحة من عملية التحميل الخاصة بها. عند اكتمال تحميل الصفحة، ينشئ معالج الأحداث المزيد من مستمعي الأحداث لمراقبة تحميل الخريطة، وإعطاء وظائف لأزرار البحث وموقعي.

  2. عند اختيار المستخدم زر البحث، أو يكتب موقعًا في مربع البحث ثم يضغط على إدخال، يبدأ بحث غامض مقابل استعلام المستخدم. تمرر التعليمات البرمجية في صفيف من قيم ISO 2 للبلد/المنطقة إلى countrySet خيار تقييد نتائج البحث بتلك البلدان/المناطق. يساعد قصر البلدان / المناطق على البحث في زيادة دقة النتائج التي يتم إرجاعها.

  3. بمجرد اكتمال البحث، تستخدم نتيجة الموقع الأولى كمحور التركيز الأساسي للخريطة. عندما يحدد المستخدم زر الموقع الخاص بي، تسترد التعليمات البرمجية موقع المستخدم باستخدام واجهة برمجة تطبيقات الموقع الجغرافي HTML5 المضمنة في المستعرض. بمجرد استرداد الموقع، تقوم التعليمات البرمجية بتوسيط الخريطة فوق موقع المستخدم.

لإضافة JavaScript:

  1. افتح index.js.

  2. أضف خيارات عامة لتسهيل تحديث الإعدادات. حدد المتغيرات للخريطة والنافذة المنبثقة ومصدر البيانات وطبقة الأيقونة وعلامة HTML. قم بتعيين علامة HTML للإشارة إلى مركز منطقة البحث. وحدد مثيلاً لعميل خدمة بحث Azure Maps.

    //The maximum zoom level to cluster data point data on the map.
    var maxClusterZoomLevel = 11;
    
    //The URL to the store location data.
    var storeLocationDataUrl = 'data/ContosoCoffee.txt';
    
    //The URL to the icon image. 
    var iconImageUrl = 'images/CoffeeIcon.png';
    
    //An array of country region ISO2 values to limit searches to.
    var countrySet = ['US', 'CA', 'GB', 'FR','DE','IT','ES','NL','DK'];      
    
    //
    var map, popup, datasource, iconLayer, centerMarker;
    
    // Used in function updateListItems
    var listItemTemplate = '<div class="listItem" onclick="itemSelected(\'{id}\')"><div class="listItem-title">{title}</div>{city}<br />Open until {closes}<br />{distance} miles away</div>';
    
    
  3. أضف رمز التهيئة التالي. تأكد من استبدال <Your Azure Maps Key> بمفتاح اشتراك خرائط Azure.

    تلميح

    عند استخدام النوافذ المنبثقة، من الأفضل إنشاء مثيل واحد Popup وإعادة استخدام المثيل عن طريق تحديث محتواه وموضعه. Popupلكل مثيل تضيفه إلى التعليمات البرمجية الخاصة بك، تتم إضافة عناصر DOM متعددة إلى الصفحة. كلما زاد عدد عناصر DOM الموجودة على الصفحة، زاد عدد الأشياء التي يجب على المتصفح تتبعها. إذا كان هناك الكثير من العناصر، فقد يصبح المستعرض بطيئًا.

    
    function initialize() {
        //Initialize a map instance.
        map = new atlas.Map('myMap', {
            center: [-90, 40],
            zoom: 2,
    
            //Add your Azure Maps subscription key to the map SDK.
            authOptions: {
                authType: 'subscriptionKey',
                subscriptionKey: '<Your Azure Maps Key>'
            }
        });
    
        //Create a pop-up window, but leave it closed so we can update it and display it later.
        popup = new atlas.Popup();
    
        //If the user selects the search button, geocode the value the user passed in.
        document.getElementById('searchBtn').onclick = performSearch;
    
        //If the user presses Enter in the search box, perform a search.
        document.getElementById('searchTbx').onkeyup = function(e) {
            if (e.keyCode === 13) {
                performSearch();
            }
        };
    
        //If the user selects the My Location button, use the Geolocation API to get the user's location. Center and zoom the map on that location.
        document.getElementById('myLocationBtn').onclick = setMapToUserLocation;
    
        //Wait until the map resources are ready.
        map.events.add('ready', function() {
    
            //Add your maps post load functionality.
    
        });
    }
    
    function performSearch() {
        var query = document.getElementById('searchTbx').value;
        //Pass in the array of country/region ISO2 for which we want to limit the search to.
        var url = `https://atlas.microsoft.com/search/fuzzy/json?api-version=1.0&countrySet=${countrySet}&query=${query}&view=Auto`;
    
        //Perform a fuzzy search on the users query.
        fetch(url, {
            headers: {
                "Subscription-Key": map.authentication.getToken()
            }
        })
        .then((response) => response.json())
        .then((response) => {
            if (Array.isArray(response.results) && response.results.length > 0) {
                var result = response.results[0];
                var bbox = [
                    result.viewport.topLeftPoint.lon,
                    result.viewport.btmRightPoint.lat,
                    result.viewport.btmRightPoint.lon,
                    result.viewport.topLeftPoint.lat
                ];
                //Set the camera to the bounds of the first result.
                map.setCamera({
                    bounds: bbox,
                    padding: 40
                });
            } else {
                document.getElementById('listPanel').innerHTML = '<div class="statusMessage">Unable to find the location you searched for.</div>';
            }
        });
    }
    
    function setMapToUserLocation() {
        //Request the user's location.
        navigator.geolocation.getCurrentPosition(function(position) {
            //Convert the geolocation API position into a longitude/latitude position value the map can understand and center the map over it.
            map.setCamera({
                center: [position.coords.longitude, position.coords.latitude],
                zoom: maxClusterZoomLevel + 1
            });
        }, function(error) {
            //If an error occurs when trying to access the users position information, display an error message.
            switch (error.code) {
                case error.PERMISSION_DENIED:
                    alert('User denied the request for geolocation.');
                    break;
                case error.POSITION_UNAVAILABLE:
                    alert('Position information is unavailable.');
                    break;
                case error.TIMEOUT:
                    alert('The request to get user position timed out.');
                    break;
                case error.UNKNOWN_ERROR:
                    alert('An unknown error occurred.');
                    break;
            }
        });
    }
    
    //Initialize the application when the page is loaded.
    window.onload = initialize;
    
  4. في معالج أحداث الخريطةready، أضف عنصر تحكم تكبير / تصغير وعلامة HTML لعرض مركز منطقة البحث.

    //Add a zoom control to the map.
    map.controls.add(new atlas.control.ZoomControl(), {
        position: 'top-right'
    });
    
    //Add an HTML marker to the map to indicate the center to use for searching.
    centerMarker = new atlas.HtmlMarker({
        htmlContent: '<div class="mapCenterIcon"></div>',
        position: map.getCamera().center
    });
    
    map.markers.add(centerMarker);
    
  5. في معالج الأحداث ready على الخريطة، أضف مصدر بيانات. ثم قم بإجراء مكالمة لتحميل مجموعة البيانات وتحليلها. تمكين التجميع على مصدر البيانات. تجميع مجموعات مصادر البيانات تعمل على تداخل النقاط معًا في نظام مجموعة. عندما يقوم المستخدم بالتكبير، تنفصل المجموعات إلى نقاط فردية. يوفر هذا السلوك تجربة مستخدم أفضل ويحسن الأداء.

    //Create a data source, add it to the map, and then enable clustering.
    datasource = new atlas.source.DataSource(null, {
        cluster: true,
        clusterMaxZoom: maxClusterZoomLevel - 1
    });
    
    map.sources.add(datasource);
    
    //Load all the store data now that the data source has been defined.  
    loadStoreData();
    
  6. بعد تحميل مجموعة البيانات في معالج الأحداثready على الخريطة، حدد مجموعة من الطبقات لعرض البيانات. طبقة فقاعية تعرض نقاط بيانات مجمعة. تعرض طبقة الرمز عدد النقاط في كل مجموعة فوق طبقة الفقاعة. طبقة الرمز الثاني تعرض رمزًا مخصصًا للمواقع الفردية على الخريطة.

    أضف mouseover أحداثا و mouseout إلى طبقات الفقاعة والأيقونة لتغيير مؤشر الماوس عندما يقوم المستخدم بالمرور فوق مجموعة أو أيقونة على الخريطة. click إضافة حدث إلى طبقة فقاعة نظام المجموعة. يقوم هذا click الحدث بتكبير الخريطة على مستويين وتوسيط الخريطة عبر نظام مجموعة عندما يحدد المستخدم أي نظام مجموعة. click إضافة حدث إلى طبقة الأيقونة. يعرض هذا click الحدث نافذة منبثقة تعرض تفاصيل المقهى عندما يحدد المستخدم أيقونة موقع فردي. أضف حدثًا إلى الخريطة للمراقبة عند انتهاء نقل الخريطة. عند تشغيل هذا الحدث، قم بتحديث العناصر في لوحة القائمة.

    //Create a bubble layer to render clustered data points.
    var clusterBubbleLayer = new atlas.layer.BubbleLayer(datasource, null, {
        radius: 12,
        color: '#007faa',
        strokeColor: 'white',
        strokeWidth: 2,
        filter: ['has', 'point_count'] //Only render data points that have a point_count property; clusters have this property.
    });
    
    //Create a symbol layer to render the count of locations in a cluster.
    var clusterLabelLayer = new atlas.layer.SymbolLayer(datasource, null, {
        iconOptions: {
            image: 'none' //Hide the icon image.
        },
    
        textOptions: {
            textField: ['get', 'point_count_abbreviated'],
            size: 12,
            font: ['StandardFont-Bold'],
            offset: [0, 0.4],
            color: 'white'
        }
    });
    
    map.layers.add([clusterBubbleLayer, clusterLabelLayer]);
    
    //Load a custom image icon into the map resources.
    map.imageSprite.add('myCustomIcon', iconImageUrl).then(function() {
    
       //Create a layer to render a coffee cup symbol above each bubble for an individual location.
       iconLayer = new atlas.layer.SymbolLayer(datasource, null, {
           iconOptions: {
               //Pass in the ID of the custom icon that was loaded into the map resources.
               image: 'myCustomIcon',
    
               //Optionally, scale the size of the icon.
               font: ['SegoeUi-Bold'],
    
               //Anchor the center of the icon image to the coordinate.
               anchor: 'center',
    
               //Allow the icons to overlap.
               allowOverlap: true
           },
    
           filter: ['!', ['has', 'point_count']] //Filter out clustered points from this layer.
       });
    
       map.layers.add(iconLayer);
    
       //When the mouse is over the cluster and icon layers, change the cursor to a pointer.
       map.events.add('mouseover', [clusterBubbleLayer, iconLayer], function() {
           map.getCanvasContainer().style.cursor = 'pointer';
       });
    
       //When the mouse leaves the item on the cluster and icon layers, change the cursor back to the default (grab).
       map.events.add('mouseout', [clusterBubbleLayer, iconLayer], function() {
           map.getCanvasContainer().style.cursor = 'grab';
       });
    
       //Add a click event to the cluster layer. When the user selects a cluster, zoom into it by two levels.  
       map.events.add('click', clusterBubbleLayer, function(e) {
           map.setCamera({
               center: e.position,
               zoom: map.getCamera().zoom + 2
           });
       });
    
       //Add a click event to the icon layer and show the shape that was selected.
       map.events.add('click', iconLayer, function(e) {
           showPopup(e.shapes[0]);
       });
    
       //Add an event to monitor when the map has finished rendering.
       map.events.add('render', function() {
           //Update the data in the list.
           updateListItems();
       });
    });
    
  7. عند الحاجة إلى مجموعة بيانات coffee shop، يجب تنزيلها أولاً. بمجرد التنزيل، يجب تقسيم الملف إلى أسطر. السطر الأول يحتوي على معلومات رأس الصفحة. لتسهيل متابعة التعليمات البرمجية، نقوم بتحليل رأس الصفحة في كائن، والذي يمكننا استخدامه بعد ذلك للبحث عن فهرس الخلية لكل خاصية. بعد السطر الأول، مرر عبر الخطوط المتبقية وأنشئ ميزة نقطية. أضف ميزة النقطة إلى مصدر البيانات. وأخيرًا، قم بتحديث لوحة القائمة.

    function loadStoreData() {
    
    //Download the store location data.
    fetch(storeLocationDataUrl)
        .then(response => response.text())
        .then(function(text) {
    
            //Parse the tab-delimited file data into GeoJSON features.
            var features = [];
    
            //Split the lines of the file.
            var lines = text.split('\n');
    
            //Grab the header row.
            var row = lines[0].split('\t');
    
            //Parse the header row and index each column to make the code for parsing each row easier to follow.
            var header = {};
            var numColumns = row.length;
            for (var i = 0; i < row.length; i++) {
                header[row[i]] = i;
            }
    
            //Skip the header row and then parse each row into a GeoJSON feature.
            for (var i = 1; i < lines.length; i++) {
                row = lines[i].split('\t');
    
                //Ensure that the row has the correct number of columns.
                if (row.length >= numColumns) {
    
                    features.push(new atlas.data.Feature(new atlas.data.Point([parseFloat(row[header['Longitude']]), parseFloat(row[header['Latitude']])]), {
                        AddressLine: row[header['AddressLine']],
                        City: row[header['City']],
                        Municipality: row[header['Municipality']],
                        AdminDivision: row[header['AdminDivision']],
                        Country: row[header['Country']],
                        PostCode: row[header['PostCode']],
                        Phone: row[header['Phone']],
                        StoreType: row[header['StoreType']],
                        IsWiFiHotSpot: (row[header['IsWiFiHotSpot']].toLowerCase() === 'true') ? true : false,
                        IsWheelchairAccessible: (row[header['IsWheelchairAccessible']].toLowerCase() === 'true') ? true : false,
                        Opens: parseInt(row[header['Opens']]),
                        Closes: parseInt(row[header['Closes']])
                    }));
                }
            }
    
            //Add the features to the data source.
            datasource.add(new atlas.data.FeatureCollection(features));
    
            //Initially, update the list items.
            updateListItems();
        });
    }
    
  8. عند تحديث لوحة القائمة، يتم حساب المسافة. هذه المسافة من وسط الخريطة إلى جميع المعالم النقطية في طريقة عرض الخريطة الحالية. ثم يتم فرز الميزات حسب المسافة. يتم إنشاء HTML لعرض كل موقع في لوحة القائمة.

    var listItemTemplate = '<div class="listItem" onclick="itemSelected(\'{id}\')"><div class="listItem-title">{title}</div>{city}<br />Open until {closes}<br />{distance} miles away</div>';
    
    function updateListItems() {
        //Hide the center marker.
        centerMarker.setOptions({
            visible: false
        });
    
        //Get the current camera and view information for the map.
        var camera = map.getCamera();
        var listPanel = document.getElementById('listPanel');
    
        //Check to see if the user is zoomed out a substantial distance. If they are, tell them to zoom in and to perform a search or select the My Location button.
        if (camera.zoom < maxClusterZoomLevel) {
            //Close the pop-up window; clusters might be displayed on the map.  
            popup.close(); 
            listPanel.innerHTML = '<div class="statusMessage">Search for a location, zoom the map, or select the My Location button to see individual locations.</div>';
        } else {
            //Update the location of the centerMarker property.
            centerMarker.setOptions({
                position: camera.center,
                visible: true
            });
    
            //List the ten closest locations in the side panel.
            var html = [], properties;
    
            /*
            Generating HTML for each item that looks like this:
            <div class="listItem" onclick="itemSelected('id')">
                <div class="listItem-title">1 Microsoft Way</div>
                Redmond, WA 98052<br />
                Open until 9:00 PM<br />
                0.7 miles away
            </div>
            */
    
            //Get all the shapes that have been rendered in the bubble layer. 
            var data = map.layers.getRenderedShapes(map.getCamera().bounds, [iconLayer]);
    
            //Create an index of the distances of each shape.
            var distances = {};
    
            data.forEach(function (shape) {
                if (shape instanceof atlas.Shape) {
    
                    //Calculate the distance from the center of the map to each shape and store in the index. Round to 2 decimals.
                    distances[shape.getId()] = Math.round(atlas.math.getDistanceTo(camera.center, shape.getCoordinates(), 'miles') * 100) / 100;
                }
            });
    
            //Sort the data by distance.
            data.sort(function (x, y) {
                return distances[x.getId()] - distances[y.getId()];
            });
    
            data.forEach(function(shape) {
                properties = shape.getProperties();
                html.push('<div class="listItem" onclick="itemSelected(\'', shape.getId(), '\')"><div class="listItem-title">',
                properties['AddressLine'],
                '</div>',
                //Get a formatted addressLine2 value that consists of City, Municipality, AdminDivision, and PostCode.
                getAddressLine2(properties),
                '<br />',
    
                //Convert the closing time to a format that is easier to read.
                getOpenTillTime(properties),
                '<br />',
    
                //Get the distance of the shape.
                distances[shape.getId()],
                ' miles away</div>');
            });
    
            listPanel.innerHTML = html.join('');
    
            //Scroll to the top of the list panel in case the user has scrolled down.
            listPanel.scrollTop = 0;
        }
    }
    
    //This converts a time that's in a 24-hour format to an AM/PM time or noon/midnight string.
    function getOpenTillTime(properties) {
        var time = properties['Closes'];
        var t = time / 100;
        var sTime;
    
        if (time === 1200) {
            sTime = 'noon';
        } else if (time === 0 || time === 2400) {
            sTime = 'midnight';
        } else {
            sTime = Math.round(t) + ':';
    
            //Get the minutes.
            t = (t - Math.round(t)) * 100;
    
            if (t === 0) {
                sTime += '00';
            } else if (t < 10) {
                sTime += '0' + t;
            } else {
                sTime += Math.round(t);
            }
    
            if (time < 1200) {
                sTime += ' AM';
            } else {
                sTime += ' PM';
            }
        }
    
        return 'Open until ' + sTime;
    }
    
    //Create an addressLine2 string that contains City, Municipality, AdminDivision, and PostCode.
    function getAddressLine2(properties) {
        var html = [properties['City']];
    
        if (properties['Municipality']) {
            html.push(', ', properties['Municipality']);
        }
    
        if (properties['AdminDivision']) {
            html.push(', ', properties['AdminDivision']);
        }
    
        if (properties['PostCode']) {
            html.push(' ', properties['PostCode']);
        }
    
        return html.join('');
    }
    
  9. عندما يحدد المستخدم عنصرًا في لوحة القائمة، يتم استرداد الشكل الذي يرتبط به العنصر من مصدر البيانات. يتم إنشاء نافذة منبثقة تستند إلى معلومات الخاصية المخزنة في الشكل. تتمحور الخريطة فوق الشكل. إذا كان عرض الخريطة أقل من 700 بكسل، تتم إزاحة عرض الخريطة بحيث تكون النافذة المنبثقة مرئية.

    //When a user selects a result in the side panel, look up the shape by its ID value and display the pop-up window.
    function itemSelected(id) {
        //Get the shape from the data source by using its ID.  
        var shape = datasource.getShapeById(id);
        showPopup(shape);
    
        //Center the map over the shape on the map.
        var center = shape.getCoordinates();
        var offset;
    
        //If the map is fewer than 700 pixels wide, then the layout is set for small screens.
        if (map.getCanvas().width < 700) {
            //When the map is small, offset the center of the map relative to the shape so that there is room for the popup to appear.
            offset = [0, -80];
        }
    
        map.setCamera({
            center: center,
            centerOffset: offset
        });
    }
    
    function showPopup(shape) {
        var properties = shape.getProperties();
    
        /* Generating HTML for the pop-up window that looks like this:
    
            <div class="storePopup">
                <div class="popupTitle">
                    3159 Tongass Avenue
                    <div class="popupSubTitle">Ketchikan, AK 99901</div>
                </div>
                <div class="popupContent">
                    Open until 22:00 PM<br/>
                    <img title="Phone Icon" src="images/PhoneIcon.png">
                    <a href="tel:1-800-XXX-XXXX">1-800-XXX-XXXX</a>
                    <br>Amenities:
                    <img title="Wi-Fi Hotspot" src="images/WiFiIcon.png">
                    <img title="Wheelchair Accessible" src="images/WheelChair-small.png">
                </div>
            </div>
        */
    
         //Calculate the distance from the center of the map to the shape in miles, round to 2 decimals.
        var distance = Math.round(atlas.math.getDistanceTo(map.getCamera().center, shape.getCoordinates(), 'miles') * 100)/100;
    
        var html = ['<div class="storePopup">'];
        html.push('<div class="popupTitle">',
            properties['AddressLine'],
            '<div class="popupSubTitle">',
            getAddressLine2(properties),
            '</div></div><div class="popupContent">',
    
            //Convert the closing time to a format that's easier to read.
            getOpenTillTime(properties),
    
            //Add the distance information.  
            '<br/>', distance,
            ' miles away',
            '<br /><img src="images/PhoneIcon.png" title="Phone Icon"/><a href="tel:',
            properties['Phone'],
            '">',  
            properties['Phone'],
            '</a>'
        );
    
        if (properties['IsWiFiHotSpot'] || properties['IsWheelchairAccessible']) {
            html.push('<br/>Amenities: ');
    
            if (properties['IsWiFiHotSpot']) {
                html.push('<img src="images/WiFiIcon.png" title="Wi-Fi Hotspot"/>');
            }
    
            if (properties['IsWheelchairAccessible']) {
                html.push('<img src="images/WheelChair-small.png" title="Wheelchair Accessible"/>');
            }
        }
    
        html.push('</div></div>');
    
        //Update the content and position of the pop-up window for the specified shape information.
        popup.setOptions({
    
            //Create a table from the properties in the feature.
            content:  html.join(''),
            position: shape.getCoordinates()
        });
    
        //Open the pop-up window.
        popup.open(map);
    }
    

الآن، لديك محدد موقع مخزن يعمل بكامل طاقته. تأكد من فتح ملف index.html في مستعرض ويب. عندما تظهر المجموعات على الخريطة، يمكنك البحث عن موقع باستخدام أي من الطرق المذكورة أدناه:

  1. مربع البحث.
  2. تحديد زر My Location
  3. تحديد نظام المجموعة
  4. تكبير الخريطة لرؤية المواقع الفردية.

في المرة الأولى التي يحدد فيها المستخدم الزر "My Location"، يعرض المتصفح تحذيرًا أمنيًا يطلب الإذن للوصول إلى موقع المستخدم. إذا وافق المستخدم على مشاركة موقعه، فستقوم الخريطة بتكبير موقع المستخدم، وستظهر المقاهي القريبة.

لقطة شاشة لطلب المتصفح للوصول إلى موقع المستخدم

عندما تقوم بالتكبير بشكل قريب بدرجة كافية في منطقة بها مواقع المقاهي، تنفصل المجموعات في مواقع فردية. حدد أحد الرموز الموجودة على الخريطة أو حدد عنصرًا في اللوحة الجانبية لمشاهدة نافذة منبثقة. تعرض النافذة المنبثقة معلومات عن الموقع المحدد.

لقطة شاشة لمحدد موقع المتجر النهائي.

إذا قمت بتغيير حجم نافذة المتصفح إلى أقل من 700 بكسل عرضًا أو قمت بفتح التطبيق على جهاز محمول، يتغير التخطيط ليكون أكثر ملاءمة للشاشات الأصغر.

لقطة شاشة لإصدار الشاشة الصغيرة من محدد موقع المتجر

في هذا البرنامج التعليمي، تعلّمت كيفية إنشاء محدد موقع مخزن أساسي باستخدام Azure Maps. قد يحتوي محدد موقع المتجر الذي تقوم بإنشائه في هذا البرنامج التعليمي على جميع الوظائف التي تحتاجها. يمكنك إضافة ميزات إلى محدد موقع متجرك أو استخدام المزيد من الميزات المتقدمة لتجربة مستخدم أكثر تخصيصًا:

معلومات إضافية

الخطوات التالية

لمشاهدة المزيد من أمثلة التعليمات البرمجية وتجربة ترميز تفاعلية: