Hello andreip,
I'd like you to do a code review. Please execute
g4 diff -c 8176910
or point your web browser to
http://mondrian/8176910
to review the following code:
Change 8176910 by [EMAIL PROTECTED] on 2008/09/04 11:47:40 *pending*
Adds access_token to Geolocation JSON protocol.
Also updates names of accuracy fields.
Jsut a heads up - this is not yet fully tested until the server is
updated to handle the new protocol.
R=andreip
[EMAIL PROTECTED]
DELTA=214 (166 added, 9 deleted, 39 changed)
OCL=8176910
Affected files ...
... //depot/googleclient/gears/opensource/gears/geolocation/geolocation_db.cc#1
edit
... //depot/googleclient/gears/opensource/gears/geolocation/geolocation_db.h#2
edit
...
//depot/googleclient/gears/opensource/gears/geolocation/geolocation_test.cc#22
edit
...
//depot/googleclient/gears/opensource/gears/geolocation/network_location_provider.cc#23
edit
...
//depot/googleclient/gears/opensource/gears/geolocation/network_location_provider.h#15
edit
...
//depot/googleclient/gears/opensource/gears/geolocation/network_location_request.cc#24
edit
...
//depot/googleclient/gears/opensource/gears/geolocation/network_location_request.h#6
edit
...
//depot/googleclient/gears/opensource/gears/test/testcases/internal_tests.js#34
edit
214 delta lines: 166 added, 9 deleted, 39 changed
Also consider running:
g4 lint -c 8176910
which verifies that the changelist doesn't introduce new style violations.
If you can't do the review, please let me know as soon as possible. During
your review, please ensure that all new code has corresponding unit tests and
that existing unit tests are updated appropriately. Visit
http://www/eng/code_review.html for more information.
This is a semiautomated message from "g4 mail". Complaints or suggestions?
Mail [EMAIL PROTECTED]
Change 8176910 by [EMAIL PROTECTED] on 2008/09/04 11:47:40 *pending*
Adds access_token to Geolocation JSON protocol.
Also updates names of accuracy fields.
Affected files ...
... //depot/googleclient/gears/opensource/gears/geolocation/geolocation_db.cc#1
edit
... //depot/googleclient/gears/opensource/gears/geolocation/geolocation_db.h#2
edit
...
//depot/googleclient/gears/opensource/gears/geolocation/geolocation_test.cc#22
edit
...
//depot/googleclient/gears/opensource/gears/geolocation/network_location_provider.cc#23
edit
...
//depot/googleclient/gears/opensource/gears/geolocation/network_location_provider.h#15
edit
...
//depot/googleclient/gears/opensource/gears/geolocation/network_location_request.cc#24
edit
...
//depot/googleclient/gears/opensource/gears/geolocation/network_location_request.h#6
edit
...
//depot/googleclient/gears/opensource/gears/test/testcases/internal_tests.js#34
edit
====
//depot/googleclient/gears/opensource/gears/geolocation/geolocation_db.cc#1 -
c:\MyDocs\Gears2/googleclient/gears/opensource/gears/geolocation/geolocation_db.cc
====
# action=edit type=text
--- googleclient/gears/opensource/gears/geolocation/geolocation_db.cc
2008-09-01 13:10:55.000000000 +0100
+++ googleclient/gears/opensource/gears/geolocation/geolocation_db.cc
2008-09-05 15:54:58.000000000 +0100
@@ -27,8 +27,9 @@
static const char16 *kDatabaseName = STRING16(L"geolocation.db");
static const char16 *kVersionTableName = STRING16(L"VersionInfo");
+static const char16 *kAccessTokenTableName = STRING16(L"AccessTokens");
static const char16 *kVersionKey = STRING16(L"Version");
-static const int kCurrentVersion = 1;
+static const int kCurrentVersion = 2;
const ThreadLocals::Slot GeolocationDB::kThreadLocalKey =
ThreadLocals::Alloc();
@@ -36,7 +37,8 @@
GeolocationDB::GeolocationDB()
: version_table_(&db_, kVersionTableName),
- position_table_(&db_) {
+ position_table_(&db_),
+ access_token_table_(&db_, kAccessTokenTableName) {
}
//static
@@ -69,6 +71,17 @@
return position_table_.GetPosition(name, position);
}
+bool GeolocationDB::StoreAccessToken(const std::string16 &server_url,
+ const std::string16 &access_token) {
+ return access_token_table_.SetString(server_url.c_str(),
+ access_token.c_str());
+}
+
+bool GeolocationDB::RetrieveAccessToken(const std::string16 &server_url,
+ std::string16 *access_token) {
+ return access_token_table_.GetString(server_url.c_str(), access_token);
+}
+
bool GeolocationDB::Create() {
ASSERT_SINGLE_THREAD();
@@ -81,7 +94,9 @@
return false;
}
- if (!version_table_.MaybeCreateTable() || !position_table_.Create()) {
+ if (!version_table_.MaybeCreateTable() ||
+ !position_table_.Create() ||
+ !access_token_table_.MaybeCreateTable()) {
return false;
}
@@ -91,6 +106,12 @@
}
return transaction.Commit();
+}
+
+bool GeolocationDB::UpgradeVersion1ToVersion2() {
+ // Version 2 adds the access token table.
+ return access_token_table_.MaybeCreateTable() &&
+ version_table_.SetInt(kVersionKey, 2);
}
bool GeolocationDB::Init() {
@@ -128,9 +149,12 @@
if (!Create()) {
return false;
}
+ } else if (1 == version) {
+ if (!UpgradeVersion1ToVersion2()) {
+ return false;
+ }
} else {
- // If the database schema is modified in the future, upgrade the
- // database here. For now, this should never happen.
+ // This should never happen.
assert(false);
return false;
}
==== //depot/googleclient/gears/opensource/gears/geolocation/geolocation_db.h#2
-
c:\MyDocs\Gears2/googleclient/gears/opensource/gears/geolocation/geolocation_db.h
====
# action=edit type=text
--- googleclient/gears/opensource/gears/geolocation/geolocation_db.h
2008-09-01 13:10:55.000000000 +0100
+++ googleclient/gears/opensource/gears/geolocation/geolocation_db.h
2008-09-05 14:55:30.000000000 +0100
@@ -40,6 +40,11 @@
bool StorePosition(const std::string16 &name, const Position &position);
bool RetrievePosition(const std::string16 &name, Position *position);
+ bool StoreAccessToken(const std::string16 &server_url,
+ const std::string16 &access_token);
+ bool RetrieveAccessToken(const std::string16 &server_url,
+ std::string16 *access_token);
+
// The key used to cache instances of GeolocationDB in ThreadLocals.
static const ThreadLocals::Slot kThreadLocalKey;
@@ -49,6 +54,8 @@
// Creates the database at the latest version.
bool Create();
+
+ bool UpgradeVersion1ToVersion2();
// Initializes the database. Must be called before other methods.
bool Init();
@@ -66,6 +73,9 @@
// Table used to store positions.
PositionTable position_table_;
+ // Table used to store access tokens for network location requests.
+ NameValueTable access_token_table_;
+
DISALLOW_EVIL_CONSTRUCTORS(GeolocationDB);
DECL_SINGLE_THREAD
};
====
//depot/googleclient/gears/opensource/gears/geolocation/geolocation_test.cc#22
-
c:\MyDocs\Gears2/googleclient/gears/opensource/gears/geolocation/geolocation_test.cc
====
# action=edit type=text
--- googleclient/gears/opensource/gears/geolocation/geolocation_test.cc
2008-09-05 18:40:20.000000000 +0100
+++ googleclient/gears/opensource/gears/geolocation/geolocation_test.cc
2008-09-05 17:41:49.000000000 +0100
@@ -297,6 +297,7 @@
scoped_refptr<BlobInterface> blob;
if (!NetworkLocationRequest::FormRequestBody(STRING16(L"www.google.com"),
+ STRING16(L"access token"),
radio_data,
wifi_data,
true,
@@ -358,12 +359,14 @@
}
Position position;
+ std::string16 access_token;
NetworkLocationRequest::GetLocationFromResponse(http_post_result,
status_code,
response_body_utf8,
timestamp,
server_url,
- &position);
+ &position,
+ &access_token);
scoped_ptr<JsObject> return_object(js_runner->NewObject());
assert(return_object.get());
@@ -385,6 +388,11 @@
L"PositionError object."));
return;
}
+ }
+
+ // Add access_token field for testing if it's set.
+ if (!access_token.empty()) {
+ return_object->SetPropertyString(STRING16(L"accessToken"), access_token);
}
context->SetReturnValue(JSPARAM_OBJECT, return_object.get());
====
//depot/googleclient/gears/opensource/gears/geolocation/network_location_provider.cc#23
-
c:\MyDocs\Gears2/googleclient/gears/opensource/gears/geolocation/network_location_provider.cc
====
# action=edit type=text
---
googleclient/gears/opensource/gears/geolocation/network_location_provider.cc
2008-09-05 18:40:21.000000000 +0100
+++
googleclient/gears/opensource/gears/geolocation/network_location_provider.cc
2008-09-05 18:30:40.000000000 +0100
@@ -27,6 +27,7 @@
#include "gears/base/common/event.h"
#include "gears/base/common/stopwatch.h" // For GetCurrentTimeMillis
+#include "gears/geolocation/geolocation_db.h"
// The maximum period of time we'll wait for a complete set of device data
// before sending the request.
@@ -35,6 +36,86 @@
static const int kBaselineMinimumRequestInterval = 1000 * 5; // 5 seconds
// The upper limit of the minimum period between network requests.
static const int kMinimumRequestIntervalLimit = 1000 * 60 * 60 * 3; // 3 hours
+
+
+// NetworkLocationRequest objects are specific to host, server URL and
language.
+// The access token should be specific to a server URL only. This class manages
+// sharing the access token between multiple NetworkLocationRequest objects,
and
+// reading and writing the value to the database for storage between sessions.
+class AccessTokenManager {
+ public:
+ static AccessTokenManager *GetInstance() {
+ return &instance_;
+ }
+
+ void Register(const std::string16 &url) {
+ MutexLock lock(&access_tokens_mutex_);
+ listener_count_.Ref();
+ // If we don't have a token for this URL in the map, try to get one from
the
+ // database.
+ if (access_tokens_.find(url) == access_tokens_.end()) {
+ GeolocationDB *db = GeolocationDB::GetDB();
+ std::string16 access_token;
+ if (db && db->RetrieveAccessToken(url, &access_token)) {
+ // Empty tokens should never be stored in the DB.
+ assert(!access_token.empty());
+ access_tokens_[url] = access_token;
+ }
+ }
+ }
+
+ void Unregister() {
+ MutexLock lock(&access_tokens_mutex_);
+ // If this is the last listener, write the tokens to the database.
+ if (listener_count_.Unref()) {
+ GeolocationDB *db = GeolocationDB::GetDB();
+ if (db) {
+ for (AccessTokenMap::const_iterator iter = access_tokens_.begin();
+ iter != access_tokens_.end();
+ iter++) {
+ if (!iter->second.empty()) {
+ db->StoreAccessToken(iter->first, iter->second);
+ }
+ }
+ }
+ }
+ }
+
+ // Returns the empty string if no token exists.
+ void GetToken(const std::string16 &url, std::string16 *access_token) {
+ assert(access_token);
+ MutexLock lock(&access_tokens_mutex_);
+ AccessTokenMap::const_iterator iter = access_tokens_.find(url);
+ if (iter == access_tokens_.end()) {
+ access_token->clear();
+ } else {
+ *access_token = iter->second;
+ }
+ }
+
+ void SetToken(const std::string16 &url, const std::string16 &access_token) {
+ MutexLock lock(&access_tokens_mutex_);
+ access_tokens_[url] = access_token;
+ }
+
+ private:
+ AccessTokenManager() {}
+ ~AccessTokenManager() {}
+
+ RefCount listener_count_;
+
+ // A map from server URL to access token.
+ typedef std::map<std::string16, std::string16> AccessTokenMap;
+ AccessTokenMap access_tokens_;
+ Mutex access_tokens_mutex_;
+
+ static AccessTokenManager instance_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(AccessTokenManager);
+};
+
+// static
+AccessTokenManager AccessTokenManager::instance_;
// The BackoffManager class is used to implement exponential back-off for
@@ -124,6 +205,8 @@
radio_data_provider_ = RadioDataProvider::Register(this);
wifi_data_provider_ = WifiDataProvider::Register(this);
+ AccessTokenManager::GetInstance()->Register(url_);
+
// Start the worker thread
Start();
}
@@ -140,6 +223,8 @@
radio_data_provider_->Unregister(this);
wifi_data_provider_->Unregister(this);
+
+ AccessTokenManager::GetInstance()->Unregister();
}
void NetworkLocationProvider::RegisterListener(
@@ -230,11 +315,17 @@
// NetworkLocationRequest::ListenerInterface implementation.
void NetworkLocationProvider::LocationResponseAvailable(
const Position &position,
- bool server_error) {
+ bool server_error,
+ const std::string16 &access_token) {
// Cache the position
position_mutex_.Lock();
position_ = position;
position_mutex_.Unlock();
+
+ // Record access_token if it's set.
+ if (!access_token.empty()) {
+ AccessTokenManager::GetInstance()->SetToken(url_, access_token);
+ }
// Get earliest time for next request.
earliest_next_request_time_ = BackoffManager::ReportResponse(url_,
@@ -399,7 +490,10 @@
BackoffManager::ReportRequest(url_);
- return request_->MakeRequest(radio_data_,
+ std::string16 access_token;
+ AccessTokenManager::GetInstance()->GetToken(url_, &access_token);
+ return request_->MakeRequest(access_token,
+ radio_data_,
wifi_data_,
request_address_,
address_language_,
====
//depot/googleclient/gears/opensource/gears/geolocation/network_location_provider.h#15
-
c:\MyDocs\Gears2/googleclient/gears/opensource/gears/geolocation/network_location_provider.h
====
# action=edit type=text
--- googleclient/gears/opensource/gears/geolocation/network_location_provider.h
2008-09-05 18:40:21.000000000 +0100
+++ googleclient/gears/opensource/gears/geolocation/network_location_provider.h
2008-09-05 17:14:54.000000000 +0100
@@ -63,7 +63,8 @@
// NetworkLocationRequest::ListenerInterface implementation.
virtual void LocationResponseAvailable(const Position &position,
- bool server_error);
+ bool server_error,
+ const std::string16 &access_token);
// Thread implementation
virtual void Run();
====
//depot/googleclient/gears/opensource/gears/geolocation/network_location_request.cc#24
-
c:\MyDocs\Gears2/googleclient/gears/opensource/gears/geolocation/network_location_request.cc
====
# action=edit type=text
--- googleclient/gears/opensource/gears/geolocation/network_location_request.cc
2008-09-05 18:40:21.000000000 +0100
+++ googleclient/gears/opensource/gears/geolocation/network_location_request.cc
2008-09-05 18:32:16.000000000 +0100
@@ -32,11 +32,14 @@
#include "third_party/jsoncpp/value.h"
#include "third_party/jsoncpp/writer.h"
-static const char *kGearsNetworkLocationProtocolVersion = "1.0.1";
-
+static const char *kGearsNetworkLocationProtocolVersion = "1.1.0";
+
+static const char *kAccessTokenString = "access_token";
static const char *kLatitudeString = "latitude";
static const char *kLongitudeString = "longitude";
static const char *kAltitudeString = "altitude";
+static const char *kAccuracyString = "accuracy";
+static const char *kAltitudeAccuracyString = "altitude_accuracy";
// Note that the corresponding JavaScript Position property is 'gearsAddress'.
static const char *kAddressString = "address";
static const char *kStreetNumberString = "street_number";
@@ -48,10 +51,6 @@
static const char *kCountryString = "country";
static const char *kCountryCodeString = "country_code";
static const char *kPostalCodeString = "postal_code";
-// TODO(steveblock): Consider updating JSON protocol field names to match those
-// used in W3C spec.
-static const char *kAccuracyString = "horizontal_accuracy";
-static const char *kAltitudeAccuracyString = "vertical_accuracy";
// Local functions
@@ -72,7 +71,8 @@
// Parses the server response body. Returns true if parsing was successful.
static bool ParseServerResponse(const std::string &response_body,
int64 timestamp,
- Position *position);
+ Position *position,
+ std::string16 *access_token);
// static
NetworkLocationRequest* NetworkLocationRequest::Create(
@@ -94,15 +94,17 @@
}
}
-bool NetworkLocationRequest::MakeRequest(const RadioData &radio_data,
+bool NetworkLocationRequest::MakeRequest(const std::string16 &access_token,
+ const RadioData &radio_data,
const WifiData &wifi_data,
bool request_address,
- std::string16 address_language,
+ const std::string16 &address_language,
double latitude,
double longitude,
int64 timestamp) {
- if (!FormRequestBody(host_name_, radio_data, wifi_data, request_address,
- address_language, latitude, longitude, &post_body_)) {
+ if (!FormRequestBody(host_name_, access_token, radio_data, wifi_data,
+ request_address, address_language, latitude, longitude,
+ &post_body_)) {
return false;
}
timestamp_ = timestamp;
@@ -111,7 +113,6 @@
}
// AsyncTask implementation.
-
void NetworkLocationRequest::Run() {
WebCacheDB::PayloadInfo payload;
// TODO(andreip): remove this once WebCacheDB::PayloadInfo.data is a Blob.
@@ -147,18 +148,21 @@
LOG(("NetworkLocationRequest::Run() : Failed to get response
body.\n"));
}
}
+ std::string16 access_token;
GetLocationFromResponse(result, payload.status_code, response_body,
- timestamp_, url_, &position);
+ timestamp_, url_, &position, &access_token);
LOG(("NetworkLocationRequest::Run() : Calling listener with position.\n"));
bool server_error =
!result || (payload.status_code >= 500 && payload.status_code < 600);
- listener_->LocationResponseAvailable(position, server_error);
+ listener_->LocationResponseAvailable(position, server_error, access_token);
}
}
// static
-bool NetworkLocationRequest::FormRequestBody(const std::string16 &host_name,
+bool NetworkLocationRequest::FormRequestBody(
+ const std::string16 &host_name,
+ const std::string16 &access_token,
const RadioData &radio_data,
const WifiData &wifi_data,
bool request_address,
@@ -175,6 +179,8 @@
}
body_object["version"] = Json::Value(kGearsNetworkLocationProtocolVersion);
AddString("host", host_name, &body_object);
+
+ AddString("access_token", access_token, &body_object);
AddInteger("home_mobile_country_code", radio_data.home_mobile_country_code,
&body_object);
@@ -253,9 +259,12 @@
int status_code,
const std::string &response_body,
int64 timestamp,
- std::string16 server_url,
- Position *position) {
+ const std::string16 &server_url,
+ Position *position,
+ std::string16 *access_token) {
assert(position);
+ assert(access_token);
+
// HttpPost can fail for a number of reasons. Most likely this is because
// we're offline, or there was no response.
if (!http_post_result) {
@@ -269,7 +278,7 @@
} else if (status_code == HttpConstants::HTTP_OK) {
// We use the timestamp from the device data that was used to generate
// this position fix.
- if (ParseServerResponse(response_body, timestamp, position)) {
+ if (ParseServerResponse(response_body, timestamp, position, access_token))
{
// The response was successfully parsed, but it may not be a valid
// position fix.
if (!position->IsGoodFix()) {
@@ -404,8 +413,11 @@
static bool ParseServerResponse(const std::string &response_body,
int64 timestamp,
- Position *position) {
+ Position *position,
+ std::string16 *access_token) {
assert(position);
+ assert(access_token);
+
if (response_body.empty()) {
LOG(("ParseServerResponse() : Response was empty.\n"));
return false;
@@ -436,17 +448,15 @@
return false;
}
- // latitude, longitude and accuracy fields are required.
- if (!IsDoubleOrInt(location, kLatitudeString) ||
- !IsDoubleOrInt(location, kLongitudeString) ||
- !IsDoubleOrInt(location, kAccuracyString)) {
- return false;
- }
- position->latitude = location[kLatitudeString].asDouble();
- position->longitude = location[kLongitudeString].asDouble();
- position->accuracy = location[kAccuracyString].asDouble();
+ // access_token, latitude, longitude and accuracy fields are required.
+ if (!GetAsDouble(location, kLatitudeString, &position->latitude) ||
+ !GetAsDouble(location, kLongitudeString, &position->longitude) ||
+ !GetAsDouble(location, kAccuracyString, &position->accuracy)) {
+ return false;
+ }
// Other fields are optional.
+ GetAsString(location, kAccessTokenString, access_token);
GetAsDouble(location, kAltitudeString, &position->altitude);
GetAsDouble(location, kAltitudeAccuracyString, &position->altitude_accuracy);
Json::Value address = location[kAddressString];
====
//depot/googleclient/gears/opensource/gears/geolocation/network_location_request.h#6
-
c:\MyDocs\Gears2/googleclient/gears/opensource/gears/geolocation/network_location_request.h
====
# action=edit type=text
--- googleclient/gears/opensource/gears/geolocation/network_location_request.h
2008-07-23 16:35:09.000000000 +0100
+++ googleclient/gears/opensource/gears/geolocation/network_location_request.h
2008-09-05 17:21:11.000000000 +0100
@@ -45,17 +45,20 @@
public:
// Updates the listener with a new position. server_error indicates whether
// was a server or network error - either no response or a 500 error code.
- virtual void LocationResponseAvailable(const Position &position,
- bool server_error) = 0;
+ virtual void LocationResponseAvailable(
+ const Position &position,
+ bool server_error,
+ const std::string16 &access_token) = 0;
};
static NetworkLocationRequest* Create(const std::string16 &url,
const std::string16 &host_name,
ListenerInterface *listener);
- bool MakeRequest(const RadioData &radio_data,
+ bool MakeRequest(const std::string16 &access_token,
+ const RadioData &radio_data,
const WifiData &wifi_data,
bool request_address,
- std::string16 address_language,
+ const std::string16 &address_language,
double latitude,
double longitude,
int64 timestamp);
@@ -73,10 +76,12 @@
const std::string16 &host_name,
ListenerInterface *listener);
virtual ~NetworkLocationRequest() {}
+
// AsyncTask implementation.
virtual void Run();
static bool FormRequestBody(const std::string16 &host_name,
+ const std::string16 &access_token,
const RadioData &radio_data,
const WifiData &wifi_data,
bool request_address,
@@ -89,8 +94,9 @@
int status_code,
const std::string &response_body,
int64 timestamp,
- std::string16 server_url,
- Position *position);
+ const std::string16 &server_url,
+ Position *position,
+ std::string16 *access_token);
int64 timestamp_; // The timestamp of the data used to make the request.
scoped_refptr<BlobInterface> post_body_;
====
//depot/googleclient/gears/opensource/gears/test/testcases/internal_tests.js#34
-
c:\MyDocs\Gears2/googleclient/gears/opensource/gears/test/testcases/internal_tests.js
====
# action=edit type=text
--- googleclient/gears/opensource/gears/test/testcases/internal_tests.js
2008-09-05 14:13:45.000000000 +0100
+++ googleclient/gears/opensource/gears/test/testcases/internal_tests.js
2008-09-05 17:39:21.000000000 +0100
@@ -378,6 +378,7 @@
if (isUsingCCTests) {
var body = internalTests.testGeolocationFormRequestBody();
var correctBody = '{ ' +
+ '"access_token" : "access token", ' +
'"address_language" : "en-GB", ' +
'"cell_towers" : [ { ' +
'"cell_id" : 23874, ' +
@@ -393,7 +394,7 @@
'}, ' +
'"radio_type" : "gsm", ' +
'"request_address" : true, ' +
- '"version" : "1.0.1", ' +
+ '"version" : "1.1.0", ' +
'"wifi_towers" : [ { ' +
'"age" : 15, ' +
'"channel" : 19, ' +
@@ -421,11 +422,12 @@
// Test good response with valid position.
var responseBody = '{ ' +
'"location" : { ' +
+ '"access_token" : "access token", ' +
'"latitude" : 53.1, ' +
'"longitude" : -0.1, ' +
'"altitude" : 30.1, ' +
- '"horizontal_accuracy" : 1200.1, ' +
- '"vertical_accuracy" : 10.1, ' +
+ '"accuracy" : 1200.1, ' +
+ '"altitude_accuracy" : 10.1, ' +
'"address" : { ' +
'"street_number": "100", ' +
'"street": "Amphibian Walkway", ' +
@@ -445,6 +447,7 @@
42, // timestamp
''); // server URL
correctPosition = new Object();
+ correctPosition.accessToken = "access token";
correctPosition.latitude = 53.1;
correctPosition.longitude = -0.1;
correctPosition.altitude = 30.1;
@@ -465,11 +468,12 @@
// We should also accept integer values for floating point fields.
var responseBody = '{ ' +
'"location" : { ' +
+ '"access_token" : "access token", ' +
'"latitude" : 53, ' +
'"longitude" : 0, ' +
'"altitude" : 30, ' +
- '"horizontal_accuracy" : 1200, ' +
- '"vertical_accuracy" : 10, ' +
+ '"accuracy" : 1200, ' +
+ '"altitude_accuracy" : 10, ' +
'"address" : { ' +
'"street_number": "100", ' +
'"street": "Amphibian Walkway", ' +