الربط الفوري التعاوني باستخدام Firebase

نظرة عامة

يوضّح لك هذا الدليل التوجيهي كيفية إنشاء خريطة تفاعلية باستخدام منصّة تطبيق Firebase. جرِّب النقر على مواقع مختلفة على الخريطة أدناه لإنشاء خريطة تمثيل لوني.

يعرض القسم أدناه الرمز الكامل الذي تحتاجه لإنشاء الخريطة في هذا البرنامج التعليمي.

/**
 * Firebase config block.
 */
var config = {
  apiKey: 'AIzaSyDX-tgWqPmTme8lqlFn2hIsqwxGL6FYPBY',
  authDomain: 'maps-docs-team.firebaseapp.com',
  databaseURL: 'https://maps-docs-team.firebaseio.com',
  projectId: 'maps-docs-team',
  storageBucket: 'maps-docs-team.appspot.com',
  messagingSenderId: '285779793579'
};

firebase.initializeApp(config);

/**
 * Data object to be written to Firebase.
 */
var data = {sender: null, timestamp: null, lat: null, lng: null};

function makeInfoBox(controlDiv, map) {
  // Set CSS for the control border.
  var controlUI = document.createElement('div');
  controlUI.style.boxShadow = 'rgba(0, 0, 0, 0.298039) 0px 1px 4px -1px';
  controlUI.style.backgroundColor = '#fff';
  controlUI.style.border = '2px solid #fff';
  controlUI.style.borderRadius = '2px';
  controlUI.style.marginBottom = '22px';
  controlUI.style.marginTop = '10px';
  controlUI.style.textAlign = 'center';
  controlDiv.appendChild(controlUI);

  // Set CSS for the control interior.
  var controlText = document.createElement('div');
  controlText.style.color = 'rgb(25,25,25)';
  controlText.style.fontFamily = 'Roboto,Arial,sans-serif';
  controlText.style.fontSize = '100%';
  controlText.style.padding = '6px';
  controlText.textContent =
      'The map shows all clicks made in the last 10 minutes.';
  controlUI.appendChild(controlText);
}

      /**
      * Starting point for running the program. Authenticates the user.
      * @param {function()} onAuthSuccess - Called when authentication succeeds.
      */
      function initAuthentication(onAuthSuccess) {
        firebase.auth().signInAnonymously().catch(function(error) {
          console.log(error.code + ', ' + error.message);
        }, {remember: 'sessionOnly'});

        firebase.auth().onAuthStateChanged(function(user) {
          if (user) {
            data.sender = user.uid;
            onAuthSuccess();
          } else {
            // User is signed out.
          }
        });
      }

      /**
       * Creates a map object with a click listener and a heatmap.
       */
      function initMap() {
        var map = new google.maps.Map(document.getElementById('map'), {
          center: {lat: 0, lng: 0},
          zoom: 3,
          styles: [{
            featureType: 'poi',
            stylers: [{ visibility: 'off' }]  // Turn off POI.
          },
          {
            featureType: 'transit.station',
            stylers: [{ visibility: 'off' }]  // Turn off bus, train stations etc.
          }],
          disableDoubleClickZoom: true,
          streetViewControl: false,
        });

        // Create the DIV to hold the control and call the makeInfoBox() constructor
        // passing in this DIV.
        var infoBoxDiv = document.createElement('div');
        makeInfoBox(infoBoxDiv, map);
        map.controls[google.maps.ControlPosition.TOP_CENTER].push(infoBoxDiv);

        // Listen for clicks and add the location of the click to firebase.
        map.addListener('click', function(e) {
          data.lat = e.latLng.lat();
          data.lng = e.latLng.lng();
          addToFirebase(data);
        });

        // Create a heatmap.
        var heatmap = new google.maps.visualization.HeatmapLayer({
          data: [],
          map: map,
          radius: 16
        });

        initAuthentication(initFirebase.bind(undefined, heatmap));
      }

      /**
       * Set up a Firebase with deletion on clicks older than expiryMs
       * @param {!google.maps.visualization.HeatmapLayer} heatmap The heatmap to
       */
      function initFirebase(heatmap) {

        // 10 minutes before current time.
        var startTime = new Date().getTime() - (60 * 10 * 1000);

        // Reference to the clicks in Firebase.
        var clicks = firebase.database().ref('clicks');

        // Listen for clicks and add them to the heatmap.
        clicks.orderByChild('timestamp').startAt(startTime).on('child_added',
          function(snapshot) {
            // Get that click from firebase.
            var newPosition = snapshot.val();
            var point = new google.maps.LatLng(newPosition.lat, newPosition.lng);
            var elapsedMs = Date.now() - newPosition.timestamp;

            // Add the point to the heatmap.
            heatmap.getData().push(point);

            // Request entries older than expiry time (10 minutes).
            var expiryMs = Math.max(60 * 10 * 1000 - elapsedMs, 0);

            // Set client timeout to remove the point after a certain time.
            window.setTimeout(function() {
              // Delete the old point from the database.
              snapshot.ref.remove();
            }, expiryMs);
          }
        );

        // Remove old data from the heatmap when a point is removed from firebase.
        clicks.on('child_removed', function(snapshot, prevChildKey) {
          var heatmapData = heatmap.getData();
          var i = 0;
          while (snapshot.val().lat != heatmapData.getAt(i).lat()
            || snapshot.val().lng != heatmapData.getAt(i).lng()) {
            i++;
          }
          heatmapData.removeAt(i);
        });
      }

      /**
       * Updates the last_message/ path with the current timestamp.
       * @param {function(Date)} addClick After the last message timestamp has been updated,
       *     this function is called with the current timestamp to add the
       *     click to the firebase.
       */
      function getTimestamp(addClick) {
        // Reference to location for saving the last click time.
        var ref = firebase.database().ref('last_message/' + data.sender);

        ref.onDisconnect().remove();  // Delete reference from firebase on disconnect.

        // Set value to timestamp.
        ref.set(firebase.database.ServerValue.TIMESTAMP, function(err) {
          if (err) {  // Write to last message was unsuccessful.
            console.log(err);
          } else {  // Write to last message was successful.
            ref.once('value', function(snap) {
              addClick(snap.val());  // Add click with same timestamp.
            }, function(err) {
              console.warn(err);
            });
          }
        });
      }

      /**
       * Adds a click to firebase.
       * @param {Object} data The data to be added to firebase.
       *     It contains the lat, lng, sender and timestamp.
       */
      function addToFirebase(data) {
        getTimestamp(function(timestamp) {
          // Add the new timestamp to the record data.
          data.timestamp = timestamp;
          var ref = firebase.database().ref('clicks').push(data, function(err) {
            if (err) {  // Data was not written to firebase.
              console.warn(err);
            }
          });
        });
      }
<div id="map"></div>
/* Always set the map height explicitly to define the size of the div
 * element that contains the map. */
#map {
  height: 100%;
}
/* Optional: Makes the sample page fill the window. */
html, body {
  height: 100%;
  margin: 0;
  padding: 0;
}
<!-- Replace the value of the key parameter with your own API key. -->
<script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyCkUOdZ5y7hMm0yrcCQoCvLwzdM6M8s5qk&libraries=visualization&callback=initMap" defer></script>
<script src="https://www.gstatic.com/firebasejs/5.3.0/firebase.js"></script>

جربه بنفسك

يمكنك تجربة هذا الرمز في JSFiddle بالنقر على رمز <> في أعلى يسار نافذة الرمز.

<!DOCTYPE html>
<html>
  <head>
    <style>
      /* Always set the map height explicitly to define the size of the div
       * element that contains the map. */
      #map {
        height: 100%;
      }
      /* Optional: Makes the sample page fill the window. */
      html, body {
        height: 100%;
        margin: 0;
        padding: 0;
      }
    </style>
  </head>
  <body>
    <div id="map"></div>

    <script src="https://www.gstatic.com/firebasejs/5.3.0/firebase-app.js"></script>
    <script src="https://www.gstatic.com/firebasejs/5.3.0/firebase-auth.js"></script>
    <script src="https://www.gstatic.com/firebasejs/5.3.0/firebase-database.js"></script>
    <script>
/**
 * Firebase config block.
 */
var config = {
  apiKey: 'AIzaSyDX-tgWqPmTme8lqlFn2hIsqwxGL6FYPBY',
  authDomain: 'maps-docs-team.firebaseapp.com',
  databaseURL: 'https://maps-docs-team.firebaseio.com',
  projectId: 'maps-docs-team',
  storageBucket: 'maps-docs-team.appspot.com',
  messagingSenderId: '285779793579'
};

firebase.initializeApp(config);

/**
 * Data object to be written to Firebase.
 */
var data = {sender: null, timestamp: null, lat: null, lng: null};

function makeInfoBox(controlDiv, map) {
  // Set CSS for the control border.
  var controlUI = document.createElement('div');
  controlUI.style.boxShadow = 'rgba(0, 0, 0, 0.298039) 0px 1px 4px -1px';
  controlUI.style.backgroundColor = '#fff';
  controlUI.style.border = '2px solid #fff';
  controlUI.style.borderRadius = '2px';
  controlUI.style.marginBottom = '22px';
  controlUI.style.marginTop = '10px';
  controlUI.style.textAlign = 'center';
  controlDiv.appendChild(controlUI);

  // Set CSS for the control interior.
  var controlText = document.createElement('div');
  controlText.style.color = 'rgb(25,25,25)';
  controlText.style.fontFamily = 'Roboto,Arial,sans-serif';
  controlText.style.fontSize = '100%';
  controlText.style.padding = '6px';
  controlText.textContent =
      'The map shows all clicks made in the last 10 minutes.';
  controlUI.appendChild(controlText);
}

      /**
      * Starting point for running the program. Authenticates the user.
      * @param {function()} onAuthSuccess - Called when authentication succeeds.
      */
      function initAuthentication(onAuthSuccess) {
        firebase.auth().signInAnonymously().catch(function(error) {
          console.log(error.code + ', ' + error.message);
        }, {remember: 'sessionOnly'});

        firebase.auth().onAuthStateChanged(function(user) {
          if (user) {
            data.sender = user.uid;
            onAuthSuccess();
          } else {
            // User is signed out.
          }
        });
      }

      /**
       * Creates a map object with a click listener and a heatmap.
       */
      function initMap() {
        var map = new google.maps.Map(document.getElementById('map'), {
          center: {lat: 0, lng: 0},
          zoom: 3,
          styles: [{
            featureType: 'poi',
            stylers: [{ visibility: 'off' }]  // Turn off POI.
          },
          {
            featureType: 'transit.station',
            stylers: [{ visibility: 'off' }]  // Turn off bus, train stations etc.
          }],
          disableDoubleClickZoom: true,
          streetViewControl: false,
        });

        // Create the DIV to hold the control and call the makeInfoBox() constructor
        // passing in this DIV.
        var infoBoxDiv = document.createElement('div');
        makeInfoBox(infoBoxDiv, map);
        map.controls[google.maps.ControlPosition.TOP_CENTER].push(infoBoxDiv);

        // Listen for clicks and add the location of the click to firebase.
        map.addListener('click', function(e) {
          data.lat = e.latLng.lat();
          data.lng = e.latLng.lng();
          addToFirebase(data);
        });

        // Create a heatmap.
        var heatmap = new google.maps.visualization.HeatmapLayer({
          data: [],
          map: map,
          radius: 16
        });

        initAuthentication(initFirebase.bind(undefined, heatmap));
      }

      /**
       * Set up a Firebase with deletion on clicks older than expiryMs
       * @param {!google.maps.visualization.HeatmapLayer} heatmap The heatmap to
       */
      function initFirebase(heatmap) {

        // 10 minutes before current time.
        var startTime = new Date().getTime() - (60 * 10 * 1000);

        // Reference to the clicks in Firebase.
        var clicks = firebase.database().ref('clicks');

        // Listen for clicks and add them to the heatmap.
        clicks.orderByChild('timestamp').startAt(startTime).on('child_added',
          function(snapshot) {
            // Get that click from firebase.
            var newPosition = snapshot.val();
            var point = new google.maps.LatLng(newPosition.lat, newPosition.lng);
            var elapsedMs = Date.now() - newPosition.timestamp;

            // Add the point to the heatmap.
            heatmap.getData().push(point);

            // Request entries older than expiry time (10 minutes).
            var expiryMs = Math.max(60 * 10 * 1000 - elapsedMs, 0);

            // Set client timeout to remove the point after a certain time.
            window.setTimeout(function() {
              // Delete the old point from the database.
              snapshot.ref.remove();
            }, expiryMs);
          }
        );

        // Remove old data from the heatmap when a point is removed from firebase.
        clicks.on('child_removed', function(snapshot, prevChildKey) {
          var heatmapData = heatmap.getData();
          var i = 0;
          while (snapshot.val().lat != heatmapData.getAt(i).lat()
            || snapshot.val().lng != heatmapData.getAt(i).lng()) {
            i++;
          }
          heatmapData.removeAt(i);
        });
      }

      /**
       * Updates the last_message/ path with the current timestamp.
       * @param {function(Date)} addClick After the last message timestamp has been updated,
       *     this function is called with the current timestamp to add the
       *     click to the firebase.
       */
      function getTimestamp(addClick) {
        // Reference to location for saving the last click time.
        var ref = firebase.database().ref('last_message/' + data.sender);

        ref.onDisconnect().remove();  // Delete reference from firebase on disconnect.

        // Set value to timestamp.
        ref.set(firebase.database.ServerValue.TIMESTAMP, function(err) {
          if (err) {  // Write to last message was unsuccessful.
            console.log(err);
          } else {  // Write to last message was successful.
            ref.once('value', function(snap) {
              addClick(snap.val());  // Add click with same timestamp.
            }, function(err) {
              console.warn(err);
            });
          }
        });
      }

      /**
       * Adds a click to firebase.
       * @param {Object} data The data to be added to firebase.
       *     It contains the lat, lng, sender and timestamp.
       */
      function addToFirebase(data) {
        getTimestamp(function(timestamp) {
          // Add the new timestamp to the record data.
          data.timestamp = timestamp;
          var ref = firebase.database().ref('clicks').push(data, function(err) {
            if (err) {  // Data was not written to firebase.
              console.warn(err);
            }
          });
        });
      }
    </script>
    <script defer
        src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&libraries=visualization&callback=initMap">
    </script>
  </body>
</html>

البدء

يمكنك تطوير إصدارك الخاص من خريطة Firebase باستخدام الرمز الوارد في هذا البرنامج التعليمي. للبدء، أنشِئ ملفًا جديدًا في محرِّر النصوص واحفظه باسم index.html.

اقرأ الأقسام التالية لفهم الرمز الذي يمكنك إضافته إلى هذا الملف.

إنشاء خريطة أساسية

يشرح هذا القسم الرمز الذي يقوم بإعداد الخريطة الأساسية. قد يكون ذلك مشابهًا لطريقة إنشاء الخرائط عند بدء استخدام Maps JavaScript API.

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

<!DOCTYPE html>
<html>
  <head>
    <style>
      #map {
        height: 100%;
      }
      html, body {
        height: 100%;
        margin: 0;
        padding: 0;
      }
    </style>
  </head>
  <body>
    <div id="map"></div>
    <script defer
        src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY
        &libraries=visualization&callback=initMap">
    </script>

    <script>
      // The JavaScript code that creates the Firebase map goes here.
    </script>

  </body>
</html>

انقر على YOUR_API_KEY في نموذج الرمز، أو اتّبِع التعليمات للحصول على مفتاح واجهة برمجة التطبيقات. استبدِل YOUR_API_KEY بمفتاح واجهة برمجة التطبيقات الخاص بتطبيقك.

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

<script>firebasemap.js</script>

يمكنك بدلاً من ذلك إدراج الرمز مباشرةً ضمن علامات النص البرمجي كما هو الحال في نموذج الرمز الكامل في بداية هذا البرنامج التعليمي.

أضِف الرمز أدناه إلى ملف firebasemap.js أو بين علامات النصوص البرمجية الفارغة في ملف index.html. هذه هي نقطة البداية التي تشغّل البرنامج عن طريق إنشاء دالة تؤدي إلى إعداد كائن الخريطة.

function initMap() {
  var map = new google.maps.Map(document.getElementById('map'), {
    center: {lat: 0, lng: 0},
    zoom: 3,
    styles: [{
      featureType: 'poi',
      stylers: [{ visibility: 'off' }]  // Turn off points of interest.
    }, {
      featureType: 'transit.station',
      stylers: [{ visibility: 'off' }]  // Turn off bus stations, train stations, etc.
    }],
    disableDoubleClickZoom: true,
    streetViewControl: false
  });
}

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

بعد تحميل واجهة برمجة التطبيقات بالكامل، تنفِّذ مَعلمة معاودة الاتصال في علامة النص البرمجي أدناه الدالة initMap() في ملف HTML.

<script defer
    src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY
    &libraries=visualization&callback=initMap">
</script>

أضف الرمز أدناه لإنشاء عنصر تحكم النص في الجزء العلوي من الخريطة.

function makeInfoBox(controlDiv, map) {
  // Set CSS for the control border.
  var controlUI = document.createElement('div');
  controlUI.style.boxShadow = 'rgba(0, 0, 0, 0.298039) 0px 1px 4px -1px';
  controlUI.style.backgroundColor = '#fff';
  controlUI.style.border = '2px solid #fff';
  controlUI.style.borderRadius = '2px';
  controlUI.style.marginBottom = '22px';
  controlUI.style.marginTop = '10px';
  controlUI.style.textAlign = 'center';
  controlDiv.appendChild(controlUI);

  // Set CSS for the control interior.
  var controlText = document.createElement('div');
  controlText.style.color = 'rgb(25,25,25)';
  controlText.style.fontFamily = 'Roboto,Arial,sans-serif';
  controlText.style.fontSize = '100%';
  controlText.style.padding = '6px';
  controlText.innerText = 'The map shows all clicks made in the last 10 minutes.';
  controlUI.appendChild(controlText);
}

أضِف الرمز أدناه داخل الدالة initMap، بعد var map، لتحميل مربّع التحكّم بالنص.

// Create the DIV to hold the control and call the makeInfoBox() constructor
// passing in this DIV.
var infoBoxDiv = document.createElement('div');
var infoBox = new makeInfoBox(infoBoxDiv, map);
infoBoxDiv.index = 1;
map.controls[google.maps.ControlPosition.TOP_CENTER].push(infoBoxDiv);
التجربة الآن

لعرض خريطة Google التي ينشئها الرمز، افتح ملف index.html في متصفّح ويب.

إعداد Firebase

لجعل هذا التطبيق تعاونيًا، يجب تخزين النقرات في قاعدة بيانات خارجية يمكن لجميع المستخدمين الوصول إليها. وتناسب قاعدة بيانات Firebase في الوقت الفعلي هذا الغرض، ولا تتطلب أي معرفة بـ SQL.

أولاً، يمكنك الاشتراك للحصول على حساب على Firebase بدون أي رسوم. إذا كنت مستخدمًا جديدًا لمنصة Firebase، سترى تطبيقًا جديدًا يحمل الاسم "تطبيقي الأول". إذا أنشأت تطبيقًا جديدًا، يمكنك تسميته باسم جديد وعنوان URL مخصّص لمنصة Firebase ينتهي بـ firebaseIO.com. على سبيل المثال، يمكنك تسمية تطبيقك باسم "خريطة Firebase لسامي" باستخدام عنوان URL https://janes-firebase-map.firebaseIO.com. يمكنك استخدام عنوان URL هذا لربط قاعدة البيانات بتطبيق JavaScript.

أضِف السطر أدناه بعد علامات <head> في ملف HTML لاستيراد مكتبة Firebase.

<script src="www.gstatic.com/firebasejs/5.3.0/firebase.js"></script>

أضِف السطر التالي إلى ملف JavaScript:

var firebase = new Firebase("<Your Firebase URL here>");

تخزين بيانات النقرات في Firebase

يشرح هذا القسم الرمز الذي يخزِّن البيانات في Firebase، معلومات عن نقرات الماوس على الخريطة.

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

/**
 * Data object to be written to Firebase.
 */
var data = {
  sender: null,
  timestamp: null,
  lat: null,
  lng: null
};

يسجّل الرمز أدناه رقم تعريف جلسة فريدًا مقابل كل نقرة، ما يساعد في التحكّم في معدّل الزيارات على الخريطة بما يتماشى مع قواعد أمان Firebase.

/**
* Starting point for running the program. Authenticates the user.
* @param {function()} onAuthSuccess - Called when authentication succeeds.
*/
function initAuthentication(onAuthSuccess) {
  firebase.auth().signInAnonymously().catch(function(error) {
      console.log(error.code + ", " + error.message);
  }, {remember: 'sessionOnly'});

  firebase.auth().onAuthStateChanged(function(user) {
    if (user) {
      data.sender = user.uid;
      onAuthSuccess();
    } else {
      // User is signed out.
    }
  });
}

يرصد القسم التالي من الرمز أدناه النقرات على الخريطة، ما يضيف "تابعًا" إلى قاعدة بيانات Firebase الخاصة بك. وعند حدوث ذلك، تحصل الدالة snapshot.val() على قيم بيانات الإدخال وتنشئ كائن LatLng جديد.

// Listen for clicks and add them to the heatmap.
clicks.orderByChild('timestamp').startAt(startTime).on('child_added',
  function(snapshot) {
    var newPosition = snapshot.val();
    var point = new google.maps.LatLng(newPosition.lat, newPosition.lng);
    heatmap.getData().push(point);
  }
);

يعمل الرمز أدناه على إعداد Firebase من أجل:

  • استمع إلى النقرات على الخريطة. وعند حدوث نقرة، يسجِّل التطبيق طابعًا زمنيًا، ثم يضيف "تابعًا" إلى قاعدة بيانات Firebase.
  • احذف أي نقرات على الخريطة مرَّ عليها أكثر من 10 دقائق في الوقت الفعلي.
/**
 * Set up a Firebase with deletion on clicks older than expirySeconds
 * @param {!google.maps.visualization.HeatmapLayer} heatmap The heatmap to
 * which points are added from Firebase.
 */
function initFirebase(heatmap) {

  // 10 minutes before current time.
  var startTime = new Date().getTime() - (60 * 10 * 1000);

  // Reference to the clicks in Firebase.
  var clicks = firebase.database().ref('clicks');

  // Listen for clicks and add them to the heatmap.
  clicks.orderByChild('timestamp').startAt(startTime).on('child_added',
    function(snapshot) {
      // Get that click from firebase.
      var newPosition = snapshot.val();
      var point = new google.maps.LatLng(newPosition.lat, newPosition.lng);
      var elapsedMs = Date.now() - newPosition.timestamp;

      // Add the point to the heatmap.
      heatmap.getData().push(point);

      // Request entries older than expiry time (10 minutes).
      var expiryMs = Math.max(60 * 10 * 1000 - elapsed, 0);
      // Set client timeout to remove the point after a certain time.
      window.setTimeout(function() {
        // Delete the old point from the database.
        snapshot.ref.remove();
      }, expiryMs);
    }
  );

  // Remove old data from the heatmap when a point is removed from firebase.
  clicks.on('child_removed', function(snapshot, prevChildKey) {
    var heatmapData = heatmap.getData();
    var i = 0;
    while (snapshot.val().lat != heatmapData.getAt(i).lat()
      || snapshot.val().lng != heatmapData.getAt(i).lng()) {
      i++;
    }
    heatmapData.removeAt(i);
  });
}

انسخ رمز JavaScript بالكامل في هذا القسم إلى ملف firebasemap.js.

إنشاء خريطة التمثيل اللوني

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

أضِف الرمز أدناه داخل الدالة initMap() لإنشاء خريطة تمثيل لوني.

// Create a heatmap.
var heatmap = new google.maps.visualization.HeatmapLayer({
  data: [],
  map: map,
  radius: 16
});

يؤدي الرمز أدناه إلى تشغيل الدوال initFirebase وaddToFirebase وgetTimestamp.

initAuthentication(initFirebase.bind(undefined, heatmap));

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

إنشاء نقاط على خريطة التمثيل اللوني

يضيف الرمز أدناه أداة معالجة الحدث داخل initMap()، بعد الرمز الذي ينشئ الخريطة. يستمع هذا الرمز إلى البيانات الواردة من كل نقرة، ويخزّن موقع النقرة في قاعدة بيانات Firebase، ويعرض النقاط على خريطة التمثيل اللوني.

// Listen for clicks and add the location of the click to firebase.
map.addListener('click', function(e) {
  data.lat = e.latLng.lat();
  data.lng = e.latLng.lng();
  addToFirebase(data);
});
التجربة الآن

انقر على المواقع الجغرافية على الخريطة لإنشاء نقاط على خريطة التمثيل اللوني.

لديك الآن تطبيق يعمل بكامل طاقته في الوقت الفعلي باستخدام Firebase و Maps JavaScript API.

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

مزيد من المعلومات

Firebase هو نظام أساسي للتطبيقات يخزّن البيانات بتنسيق JSON، وتتم مزامنته مع جميع العملاء المرتبطين في الوقت الفعلي. وهي متاحة حتى عندما يكون تطبيقك بلا اتصال بالإنترنت. يستخدم هذا البرنامج التعليمي قاعدة بيانات الوقت الفعلي الخاصة به.