إنشاء خرائط تعاونية في الوقت الفعلي باستخدام 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 لخرائط Google، ويعرض الخريطة على الشاشة الكاملة. ويحمِّل أيضًا مكتبة العروض المرئية، التي ستحتاج إليها لاحقًا في البرنامج التعليمي لإنشاء خريطة حرارية.

<!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. يسجِّل هذا العنصر بيانات مثل latLng والطابع الزمني للنقرة، بالإضافة إلى معرّف فريد للمتصفّح الذي أجرى النقرة.

/**
 * 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، وتتم مزامنتها مع جميع العملاء المتصلين في الوقت الفعلي. وتتوفّر هذه الميزة حتى عندما يكون تطبيقك غير متصل بالإنترنت. يستخدم هذا البرنامج التعليمي قاعدة بياناته في الوقت الفعلي.