Novembre 2007
Obiettivo
Il web è pieno di community incentrate su aree geografiche e interessi: persone che amano i musei, le cattedrali europee, i parchi statali e così via. Quindi, c'è sempre bisogno di uno sviluppatore (come te) che crei un sistema in cui gli utenti possano aggiungere luoghi georeferenziati a una mappa, ed è esattamente quello che faremo qui. Al termine di questo articolo, avrai un sistema in cui gli utenti possono registrarsi, accedere e aggiungere luoghi georeferenziati. Il sistema utilizzerà AJAX per il front-end, PHP per gli script lato server e Fogli Google per l'archiviazione. Se sei abituato a utilizzare i database MySQL per l'archiviazione, puoi facilmente modificare il codice qui per utilizzare un backend di database MySQL.
Questo articolo è suddiviso nei seguenti passaggi:
- Configurare il foglio di lavoro
- Utilizzare il framework Zend Gdata
- Creazione di funzioni globali
- Registrazione di un nuovo utente
- Accesso di un utente
- Consentire agli utenti di aggiungere luoghi alla mappa
- Creazione della mappa
- Conclusione
Configurare il foglio di lavoro
Utilizzeremo Fogli Google per archiviare tutti i dati di questo sistema. Dobbiamo archiviare due tipi di dati: le informazioni dell'account utente e i luoghi aggiunti dall'utente, quindi creeremo un foglio di lavoro per ogni tipo di dati. Interagiremo con i fogli di lavoro utilizzando il feed di elenco, che si basa sulla prima riga di un foglio di lavoro contenente le etichette delle colonne e su ogni riga successiva contenente i dati.
Visita docs.google.com e crea un nuovo foglio di lavoro. Rinomina il foglio di lavoro predefinito in "Users" e crea colonne denominate "user", "password" e "session". Poi aggiungi un altro foglio, rinominalo "Locations" e crea colonne denominate "user", "status", "lat", "lng" e "date". In alternativa, se non vuoi fare tutto questo lavoro manuale, scarica questo modello e importalo in Fogli Google tramite il comando File->Importa.
Le informazioni dell'account utente devono essere mantenute private (visibili solo al proprietario del foglio di lavoro), mentre i luoghi aggiunti dall'utente verranno visualizzati su una mappa visibile pubblicamente. Fortunatamente, Fogli Google ti consente di decidere in modo selettivo quali fogli di lavoro di un foglio di lavoro possono essere pubblici e quali devono rimanere privati (impostazione predefinita). Per pubblicare il foglio di lavoro "Località", fai clic sulla scheda "Pubblica", poi su "Pubblica ora", seleziona la casella di controllo "Ripubblica automaticamente" e infine seleziona "Solo foglio "Località "" nel menu a discesa "Quali parti?". Le opzioni corrette sono mostrate nello screenshot di seguito:
Utilizzo di Zend GData Framework
L'API Google Spreadsheets fornisce un'interfaccia HTTP per operazioni CRUD come il recupero, l'inserimento, l'aggiornamento e l'eliminazione di righe. Zend Framework fornisce un wrapper PHP sopra l'API (e le altre API GData) in modo da non doverti preoccupare dell'implementazione delle operazioni HTTP non elaborate. Zend Framework richiede PHP 5.
Se non lo hai già fatto, scarica il framework Zend e caricalo sul server. Il framework è disponibile qui: http://framework.zend.com/download/gdata.
Devi modificare il percorso include_path di PHP per includere la libreria Zend. Esistono diversi modi per farlo, a seconda del livello di diritti amministrativi che hai sul server. Un modo è aggiungere questa riga sopra le istruzioni require in qualsiasi file PHP che utilizza la libreria:
ini_set("include_path", ".:/usr/lib/php:/usr/local/lib/php:../../../library/");
Per provarlo, esegui la demo di Fogli inserendo questo comando nella riga di comando della cartella demos/Zend/Gdata:
php Spreadsheet-ClientLogin.php --user=YourGMailUsername --pass=YourPassword
Se funziona, dovresti visualizzare un elenco dei tuoi fogli di lavoro. Se ricevi un errore, verifica che il percorso di inclusione sia impostato correttamente e che sia installato PHP 5.
Creazione di funzioni globali
Tutti gli script PHP che scriveremo per la mappa della community utilizzeranno include, variabili e funzioni comuni, che inseriremo in un unico file.
All'inizio del file, avremo le istruzioni necessarie per includere e caricare la libreria Zend, prese dall'esempio Spreadsheets-ClientLogin.php.
Poi definiamo le costanti che verranno utilizzate in tutti i file: la chiave del foglio di lavoro e i due ID foglio. Per trovare le informazioni sul foglio di lavoro, aprilo, fai clic sulla scheda "Pubblica" e poi su "Altre opzioni di pubblicazione". Seleziona "ATOM" dall'elenco a discesa Formato file e fai clic su "Genera URL". Visualizzerai un messaggio simile a questo:
http://spreadsheets.google.com/feeds/list/o16162288751915453340.4016005092390554215/od6/public/basic
La chiave del foglio di lavoro è la lunga stringa alfanumerica dopo "/list/," e l'ID foglio di lavoro è la stringa di tre caratteri successiva. Per trovare l'ID dell'altro foglio di lavoro, seleziona l'altro foglio dal menu a discesa "Quali fogli?".
Poi creeremo tre funzioni: setupClient, getWkshtListFeed e printFeed. In setupClient, imposteremo il nome utente e la password di Gmail, ci autenticheremo con ClientLogin e restituiremo un oggetto Zend_Gdata_Spreadsheets. In getWkshtListFeed, restituiremo un feed di elenco di fogli di lavoro per una determinata chiave del foglio di lavoro e un determinato ID foglio di lavoro, con una query facoltativa sui fogli di lavoro (link). La funzione printFeed è tratta dall'esempio Spreadsheets-ClientLogin.php e potrebbe esserti utile per il debug. Prende un oggetto feed e lo stampa sullo schermo.
Il codice PHP che esegue questa operazione è mostrato di seguito (communitymap_globals.php):
<?php
ini_set("include_path", ".:/usr/lib/php:/usr/local/lib/php:../../../library/");
require_once 'Zend/Loader.php';
Zend_Loader::loadClass('Zend_Gdata');
Zend_Loader::loadClass('Zend_Gdata_ClientLogin');
Zend_Loader::loadClass('Zend_Gdata_Spreadsheets');
Zend_Loader::loadClass('Zend_Http_Client');
define("SPREADSHEET_KEY", "o16162288751915453340.4016005092390554215");
define("USER_WORKSHEET_ID", "od6");
define("LOC_WORKSHEET_ID", "od7");
function setupClient() {
$email = "your.name@gmail.com";
$password = "yourPassword";
$client = Zend_Gdata_ClientLogin::getHttpClient($email, $password,
Zend_Gdata_Spreadsheets::AUTH_SERVICE_NAME);
$gdClient = new Zend_Gdata_Spreadsheets($client);
return $gdClient;
}
function getWkshtListFeed($gdClient, $ssKey, $wkshtId, $queryString=null) {
$query = new Zend_Gdata_Spreadsheets_ListQuery();
$query->setSpreadsheetKey($ssKey);
$query->setWorksheetId($wkshtId);
if ($queryString !== null) {
$query->setSpreadsheetQuery($queryString);
}
$listFeed = $gdClient->getListFeed($query);
return $listFeed;
}
function printFeed($feed)
{
print "printing feed";
$i = 0;
foreach($feed->entries as $entry) {
if ($entry instanceof Zend_Gdata_Spreadsheets_CellEntry) {
print $entry->title->text .' '. $entry->content->text . "\n";
} else if ($entry instanceof Zend_Gdata_Spreadsheets_ListEntry) {
print $i .' '. $entry->title->text .' '. $entry->content->text . "\n";
} else {
print $i .' '. $entry->title->text . "\n";
}
$i++;
}
}
?>
Registrazione di un nuovo utente
Per registrare un nuovo utente, ci serve una pagina HTML rivolta agli utenti con campi di testo e un pulsante di invio, nonché uno script di backend PHP per aggiungere l'utente al foglio di lavoro.
Nello script PHP, includiamo prima lo script globale e poi otteniamo i valori di nome utente e password dalla variabile GET. Poi configuriamo un client Fogli e richiediamo il feed di elenco per il foglio di lavoro degli utenti con una stringa di query per limitare i risultati alle sole righe in cui la colonna del nome utente è uguale al nome utente passato nello script. Se non otteniamo righe nel risultato del feed di elenco, possiamo procedere in sicurezza sapendo che il nome utente passato è univoco. Prima di inserire una riga nel feed di elenco, creiamo un array associativo dei valori delle colonne: il nome utente, una crittografia della password utilizzando la funzione sha1 di PHP e un carattere di riempimento per la sessione. Poi chiamiamo insertRow sul client Fogli, passando l'array associativo, la chiave dei fogli di lavoro e l'ID del foglio di lavoro. Se l'oggetto restituito è un ListFeedEntry, viene visualizzato il messaggio Operazione riuscita.
Il codice PHP che esegue questa operazione è mostrato di seguito (communitymap_newuser.php):
<?php
require_once 'communitymap_globals.php';
$username = $_GET['username'];
$password = $_GET['password'];
$gdClient = setupClient();
$listFeed = getWkshtListFeed($gdClient, SPREADSHEET_KEY, USER_WORKSHEET_ID, ('user='.$username));
$totalResults = $listFeed->totalResults;
if ( $totalResults != "0") {
// Username already exists
exit;
}
$rowArray["user"] = $username;
$rowArray["password"] = sha1($password);
$rowArray["session"] = "a";
$entry = $gdClient->insertRow($rowArray, SPREADSHEET_KEY, USER_WORKSHEET_ID);
if ($entry instanceof Zend_Gdata_Spreadsheets_ListEntry) {
echo "Success!";
}
?>
Nella pagina di registrazione, possiamo includere l'API Maps in modo da poter utilizzare la relativa funzione wrapper XMLHttpRequest chiamata GDownloadUrl. Quando l'utente fa clic sul pulsante di invio, recuperiamo il nome utente e la password dai campi di testo, creiamo una stringa di parametri dai relativi valori e chiamiamo GDownloadUrl sull'URL e sui parametri dello script. Poiché inviamo informazioni sensibili, utilizziamo la versione HTTP POST di GDownloadUrl (inviando i parametri come terzo argomento anziché aggiungerli all'URL). Nella funzione di callback, verificheremo la presenza di una risposta riuscita e restituiremo un messaggio appropriato all'utente.
Di seguito sono riportati uno screenshot e il codice di una pagina di registrazione di esempio (communitymap_register.htm):
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title> Community Map - Register/Login </title>
<script src="http://maps.google.com/maps?file=api&v=2&key=abcdef"
type="text/javascript"></script>
<script type="text/javascript">
function register() {
var username = document.getElementById("username").value;
var password = document.getElementById("password").value;
var url = "communitymap_newuser.php?";
var params = "username=" + username + "&password=" + password;
GDownloadUrl(url, function(data, responseCode) {
if (data.length > 1) {
document.getElementById("message").innerHTML = "Successfully registered." +
"<a href='communitymap_login.htm'>Proceed to Login</a>.";
} else {
document.getElementById("message").innerHTML = "Username already exists. Try again.";
}
}, params);
}
</script>
</head>
<body>
<h1>Register for Community Map</h1>
<input type="text" id="username">
<input type="password" id="password">
<input type="button" onclick="register()" value="Register">
<div id="message"></div>
</body>
</html>
Accesso di un utente
Per consentire agli utenti di accedere al nostro sistema, vogliamo una pagina HTML rivolta agli utenti che richieda loro il nome utente e la password e uno script PHP per verificare le informazioni di accesso, creare un ID sessione e restituirlo alla pagina di accesso per impostare un cookie. L'utente rimarrà connesso tramite il cookie di sessione nelle pagine successive.
Nello script PHP, includiamo prima lo script globale e poi otteniamo i valori di nome utente e password dalla variabile GET. Poi configuriamo un client Fogli e richiediamo il feed di elenco per il foglio di lavoro degli utenti con una stringa di query per limitare i risultati alle sole righe in cui la colonna del nome utente è uguale al nome utente passato nello script.
Nella riga restituita, verificheremo che l'hash della password trasmessa corrisponda a quello memorizzato nel foglio di lavoro. In caso affermativo, creeremo un ID sessione utilizzando le funzioni md5, uniqid e rand. Dopodiché, aggiorneremo la riga nel foglio di lavoro con la sessione e la visualizzeremo sullo schermo se l'aggiornamento della riga va a buon fine.
Il codice PHP che esegue questa operazione è mostrato di seguito (communitymap_loginuser.php):
<?php
require_once 'communitymap_globals.php';
$username = $_POST['username'];
$password = $_POST['password'];
$gdClient = setupClient();
$listFeed = getWkshtListFeed($gdClient, SPREADSHEET_KEY, USER_WORKSHEET_ID, ('user='.$username));
$password_hash = sha1($password);
$row = $listFeed->entries[0];
$rowData = $row->getCustom();
foreach($rowData as $customEntry) {
if ($customEntry->getColumnName()=="password" && $customEntry->getText()==$password_hash) {
$updatedRowArray["user"] = $username;
$updatedRowArray["password"] = sha1($password);
$updatedRowArray["session"] = md5(uniqid(rand(), true));
$updatedRow = $gdClient->updateRow($row, $updatedRowArray);
if ($updatedRow instanceof Zend_Gdata_Spreadsheets_ListEntry) {
echo $updatedRowArray["session"];
}
}
}
?>
Nella pagina di accesso, possiamo includere di nuovo l'API Maps in modo da poter utilizzare la funzione wrapper XMLHttpRequest chiamata GDownloadUrl. Quando l'utente fa clic sul pulsante di invio, recuperiamo il nome utente e la password dai campi di testo, creiamo l'URL dello script con i parametri di query e chiamiamo GDownloadUrl sull'URL dello script. Nella funzione di callback, imposteremo un cookie con l'ID sessione restituito dallo script o genereremo un messaggio di errore se non viene restituito alcun ID. La funzione setCookie proviene da un file cookies.js basato sul tutorial JavaScript di W3C: http://www.w3schools.com/js/js_cookies.asp.
Di seguito sono riportati uno screenshot e il codice di una pagina di accesso di esempio (communitymap_login.htm):
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Community Map - Login</title>
<script src="http://maps.google.com/maps?file=api&v=2&key=abcdef"
type="text/javascript"></script>
<script src="cookies.js" type="text/javascript"></script>
<script type="text/javascript">
function login() {
var username = document.getElementById("username").value;
var password = document.getElementById("password").value;
var url = "communitymap_loginuser.php?username=" + username + "&password=" + password;
GDownloadUrl(url, function(data, responseCode) {
if (data.length > 1) {
setCookie("session", data, 5);
} else {
document.getElementById("nessage").innerHTML = "Error. Try again.";
}
});
}
</script>
</head>
<body>
<h1>Login for Community Map</h1>
<input type="text" id="username">
<input type="password" id="password">
<input type="button" onclick="login()" value="Login">
<div id="message"></div>
</body>
</html>
Consentire agli utenti di aggiungere luoghi sulla mappa
Per consentire a un utente di aggiungere luoghi alla nostra mappa, avremo bisogno di una pagina HTML rivolta agli utenti per consentire loro di fornire informazioni sulla posizione e di due script PHP: uno per verificare che l'utente abbia eseguito l'accesso tramite il cookie che abbiamo impostato e un altro per aggiungere la posizione al foglio di lavoro delle posizioni.
Nel primo script PHP che verifica se un utente ha eseguito l'accesso, includiamo prima lo script globale e poi recuperiamo il valore della sessione dalla variabile GET. Poi configuriamo un client Fogli e richiediamo il feed di elenco per il foglio di lavoro degli utenti con una stringa di query per limitare i risultati alle sole righe in cui la colonna della sessione è uguale al valore della sessione passato allo script. Quindi, scorriamo le voci personalizzate di quel feed (quelle che corrispondono alle intestazioni delle colonne) e stampiamo il nome utente corrispondente per quella sessione, se esistente.
Il codice PHP che esegue questa operazione è mostrato di seguito (communitymap_checksession.php):
<?php
require_once 'communitymap_globals.php';
$session = $_GET['session'];
$gdClient = setupClient();
$listFeed = getWkshtListFeed($gdClient, SPREADSHEET_KEY, USER_WORKSHEET_ID, ('session='.$session));
if ( count($listFeed->entries) > 0) {
$row = $listFeed->entries[0];
$rowData = $row->getCustom();
foreach($rowData as $customEntry) {
if ($customEntry->getColumnName()=="user") {
echo $customEntry->getText();
}
}
}
?>
Nel secondo script PHP che consente a un utente di aggiungere una località, replichiamo innanzitutto il codice di communitymap_checksession.php per assicurarci che l'utente sia ancora connesso e valido. Una volta ricevuto un nome utente valido dal foglio degli utenti, otteniamo i valori di luogo, latitudine e longitudine dalla variabile GET. Inseriamo tutti questi valori in un array associativo e aggiungiamo anche un valore "date" utilizzando la funzione date() di PHP, in modo da sapere quando l'utente ha aggiunto il luogo. Passiamo questo array associativo, la costante della chiave dei fogli di lavoro e la costante dell'ID del foglio di lavoro delle località alla funzione insertRow. Quindi, restituiamo "Successo" se è stata aggiunta una riga per la nuova posizione al foglio di lavoro. Se ricevi un errore in questo passaggio, è probabilmente dovuto a una mancata corrispondenza nei nomi delle intestazioni di colonna. Le chiavi nell'array associativo devono corrispondere alle intestazioni di colonna nel foglio di lavoro specificato dalla chiave del foglio di lavoro e dall'ID foglio di lavoro.
Il codice PHP che esegue questa operazione è mostrato di seguito (communitymap_addlocation.php):
<?php
require_once 'communitymap_globals.php';
$session = $_GET['session'];
$gdClient = setupClient();
$listFeed = getWkshtListFeed($gdClient, SPREADSHEET_KEY, USER_WORKSHEET_ID, ('session='.$session));
if ( count($listFeed->entries) > 0) {
$row = $listFeed->entries[0];
$rowData = $row->getCustom();
foreach($rowData as $customEntry) {
if ($customEntry->getColumnName()=="user") {
$user = $customEntry->getText();
}
}
$place = $_GET['place'];
$lat = $_GET['lat'];
$lng = $_GET['lng'];
$rowArray["user"] = $user;
$rowArray["place"] = $place;
$rowArray["lat"] = $lat;
$rowArray["lng"] = $lng;
$rowArray["date"] = date("F j, Y, g:i a");
$entry = $gdClient->insertRow($rowArray, SPREADSHEET_KEY, LOC_WORKSHEET_ID);
if ($entry instanceof Zend_Gdata_Spreadsheets_ListEntry) {
echo "Success!\n";
}
}
?>
Nella pagina di aggiunta della posizione, possiamo includere di nuovo l'API Maps in modo da poter utilizzare GDownloadUrl e creare una mappa. Dopo il caricamento della pagina, utilizziamo la funzione getCookie di cookies.js per recuperare il valore della sessione. Se la stringa di sessione è nulla o vuota, viene visualizzato un messaggio di errore. In caso contrario, chiamiamo GDownloadUrl su map.checksession.php, inviando la sessione. Se viene restituito correttamente un nome utente, mostriamo un messaggio di benvenuto all'utente, visualizziamo il modulo per aggiungere una posizione e carichiamo la mappa. Il modulo è composto da un campo di testo per l'indirizzo, una mappa e campi di testo per il nome del luogo, la latitudine e la longitudine. Se l'utente non conosce già la latitudine e la longitudine della posizione, può geocodificarla inserendo l'indirizzo nel modulo e premendo "Invia". Verrà inviata una chiamata a GClientGeocoder dell'API Maps, che posizionerà un indicatore sulla mappa se trova l'indirizzo e compilerà automaticamente i campi di testo lat/lng.
Quando l'utente è soddisfatto, può premere il pulsante "Aggiungi posizione". Poi, in JavaScript, recupereremo i valori per user, place, lat e lng e li invieremo allo script communitymap_addlocation.php con GDownloadUrl.
Se lo script restituisce un esito positivo, visualizzeremo un messaggio di esito positivo sullo schermo.
Di seguito sono riportati uno screenshot e il codice di una pagina di esempio per l'aggiunta di una località (communitymap_addlocation.htm):
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8"/>
<title>Community Map - Add a Place!</title>
<script src="http://maps.google.com/maps?file=api&v=2.x&key=abcdef" type="text/javascript"></script>
<script src="cookies.js" type="text/javascript"></script>
<script type="text/javascript">
//<![CDATA[
var map = null;
var geocoder = null;
var session = null;
function load() {
session = getCookie('session');
if (session != null && session != "") {
url = "communitymap_checksession.php?session=" + session;
GDownloadUrl(url, function(data, responseCode) {
if (data.length > 0) {
document.getElementById("message").innerHTML = "Welcome " + data;
document.getElementById("content").style.display = "block";
map = new GMap2(document.getElementById("map"));
map.setCenter(new GLatLng(37.4419, -122.1419), 13);
geocoder = new GClientGeocoder();
}
});
} else {
document.getElementById("message").innerHTML = "Error: Not logged in.";
}
}
function addLocation() {
var place = document.getElementById("place").value;
var lat = document.getElementById("lat").value;
var lng = document.getElementById("lng").value;
var url = "communitymap_addlocation.php?session=" + session + "&place=" + place +
"&lat=" + lat + "&lng=" + lng;
GDownloadUrl(url, function(data, responseCode) {
GLog.write(data);
if (data.length > 0) {
document.getElementById("message").innerHTML = "Location added.";
}
});
}
function showAddress(address) {
if (geocoder) {
geocoder.getLatLng(
address,
function(point) {
if (!point) {
alert(address + " not found");
} else {
map.setCenter(point, 13);
var marker = new GMarker(point, {draggable:true});
document.getElementById("lat").value = marker.getPoint().lat().toFixed(6);
document.getElementById("lng").value = marker.getPoint().lng().toFixed(6);
map.addOverlay(marker);
GEvent.addListener(marker, "dragend", function() {
document.getElementById("lat").value = marker.getPoint().lat().toFixed(6);
document.getElementById("lng").value = marker.getPoint().lng().toFixed(6);
});
}
}
);
}
}
//]]>
</script>
</head>
<body onload="load()" onunload="GUnload()">
<div id="message"></div>
<div id="content" style="display:none">
<form action="#" onsubmit="showAddress(this.address.value); return false">
<p>
<input type="text" size="60" name="address" value="1600 Amphitheatre Pky, Mountain View, CA" />
<input type="submit" value="Geocode!" />
</form>
</p>
<div id="map" style="width: 500px; height: 300px"></div>
Place name: <input type="text" size="20" id="place" value="" />
<br/>
Lat: <input type="text" size="20" id="lat" value="" />
<br/>
Lng: <input type="text" size="20" id="lng" value="" />
<br/>
<input type="button" onclick="addLocation()" value="Add a location" />
</form>
</div>
</body>
</html>
Creazione della mappa
Poiché hai reso pubblico il foglio di lavoro delle località nel primo passaggio, non è necessaria alcuna programmazione lato server per creare una mappa. Infatti, non è richiesta alcuna programmazione. Puoi utilizzare questa procedura guidata Fogli -> Mappa, che genererà tutto il codice necessario per la mappa. La procedura guidata scarica le voci del foglio di lavoro nella pagina aggiungendo un tag script che punta all'output JSON per il feed e specifica una funzione di callback che viene chiamata una volta scaricato il file JSON. Ulteriori informazioni sono disponibili qui.
Un esempio di codice HTML per farlo è disponibile qui: mainmap.htm. Di seguito è riportato uno screenshot:
Conclusione
A questo punto, dovresti avere il tuo sistema di mappe create dagli utenti in esecuzione sul server. Questo articolo fornisce il codice di base necessario per gli aspetti essenziali di questo sistema, ma ora che hai familiarità con la libreria Zend Spreadsheets, dovresti essere in grado di estendere il sistema per soddisfare le tue esigenze particolari. Se hai riscontrato errori durante la procedura, ricorda che puoi utilizzare il comando echo in PHP o GLog.write() dell'API Maps in JavaScript per il debug e puoi sempre pubblicare post nei forum per sviluppatori dell'API Maps o dell'API Spreadsheets per ulteriore assistenza.