Обзор
В этом руководстве показано, как создать интерактивную карту с помощью платформы приложений 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
. Этот код загружает Maps JavaScript API и делает карту полноэкранной. Он также загружает библиотеку визуализации, которая понадобится вам позже в руководстве для создания тепловой карты.
<!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
в примере кода или следуйте инструкциям, чтобы получить ключ API . Замените YOUR_API_KEY
ключом API вашего приложения.
В следующих разделах объясняется код 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 }); }
Чтобы упростить использование интерактивной тепловой карты, в приведенном выше коде используется стиль карты для отключения достопримечательностей и транзитных станций (при нажатии на которые отображается информационное окно). Он также отключает масштабирование при двойном щелчке, чтобы предотвратить случайное масштабирование. Узнайте больше о стилизации карты .
После полной загрузки API параметр обратного вызова в теге скрипта ниже выполняет функцию 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 Realtime подходит для этой цели и не требует каких-либо знаний 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 и синхронизируется со всеми подключенными клиентами в режиме реального времени. Он доступен, даже если ваше приложение отключено от сети. В этом руководстве используется база данных реального времени.