BGeolocation: add position to country mapping
Using the geonames.org API, so we will need an API key for it (similar to the one used for MLS, deployed by the buildbot) The unit tests uses the "demo" user, which is restricted to 20000 API call credits and often expired. But we cannot use our secret key here as it would need to be available to anyone running the test. If we ever get to automate running the tests on a buildserver we could probably make it use the secret username known by our buildbot instead. Change-Id: Ia16880db82555ce85505ad28e1c623f692f46be0 Reviewed-on: https://review.haiku-os.org/c/haiku/+/1873 Reviewed-by: Alex von Gluck IV <kallisti5@unixzen.com>
This commit is contained in:
parent
7100b1e1f5
commit
edc5a2174e
89
docs/user/shared/Geolocation.dox
Normal file
89
docs/user/shared/Geolocation.dox
Normal file
@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright 2019 Haiku, Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Adrien Destugues, pulkomandy@pulkomandy.tk
|
||||
*
|
||||
* Corresponds to:
|
||||
* headers/private/shared/Geolocation.h hrev53489
|
||||
* src/kits/shared/Geolocation.cpp hrev53489
|
||||
*/
|
||||
|
||||
|
||||
/*!
|
||||
\file Geolocation.h
|
||||
\ingroup shared
|
||||
\brief Geolocation and geocoding services.
|
||||
*/
|
||||
|
||||
|
||||
/*!
|
||||
\class BGeolocation
|
||||
\ingroup shared
|
||||
\brief Geolocation and geocoding services.
|
||||
|
||||
Geolocation allows to know where the computer is currently located.
|
||||
This is currently done by sending a list of Wifi networks within reach to
|
||||
a 3rd party webservice (Mozilla location services), which uses a database
|
||||
of known networks around the world to return an estimate position.
|
||||
|
||||
Geocoding is about identifying places in a way more convenient than
|
||||
latitude and longitude. This can include postal codes, country names,
|
||||
street or building names, etc. This class allows to perform both geocoding
|
||||
(finding a latitude and a longitude from a name) and reverse geocoding
|
||||
(finding a suitable name for a given position). This service is provided
|
||||
to haiku by the 3rd-party geonames.org
|
||||
|
||||
Note that most of the methods in this class perform HTTPS requests to
|
||||
3rd-parties. When using it in your application, it is good practise to
|
||||
first inform the user about what will be shared with these services, and
|
||||
why that's needed. Of course, this also means using these services requires
|
||||
internet access, and you may want to limit the number of calls, both to
|
||||
save bandwidth and time, and to not overuse the provided API keys. If you
|
||||
are going to do more than a few request per application run, you should
|
||||
provide your own API keys instead.
|
||||
|
||||
\since Haiku R1
|
||||
*/
|
||||
|
||||
|
||||
/*!
|
||||
\fn BGeolocation::BGeolocation()
|
||||
\brief Initialize a BGeolocation using the default services and API keys.
|
||||
|
||||
Official builds of Haiku include a shared API key for both Mozilla Location
|
||||
Services and geonames.org. You can use this constructor to use these keys
|
||||
in your application.
|
||||
*/
|
||||
|
||||
|
||||
/*!
|
||||
\fn BGeolocation::BGeolocation(BUrl& geolocationService,
|
||||
BUrl& geocodingService)
|
||||
\brief Initialize a BGeolocation using custom services or API keys.
|
||||
|
||||
You can use this constructor to provide your own URL to a webservice to
|
||||
use for the requests. The services have to implement the MLS and geonames
|
||||
API, respectively.
|
||||
|
||||
If you want to override only one of the two URLs, the other can be passed
|
||||
as an empty BUrl object.
|
||||
*/
|
||||
|
||||
|
||||
/*!
|
||||
\fn status_t BGeolocation::LocateSelf(float& latitude, float& longitude)
|
||||
\brief Locate the computer by detecting nearby WiFi networks.
|
||||
|
||||
This method makes a request to Mozilla Location Services.
|
||||
*/
|
||||
|
||||
|
||||
/*!
|
||||
\fn status_t BGeolocation::Country(float latitude, float longitude,
|
||||
BCountry& country)
|
||||
\brief Find which country a given set of coordinates is in.
|
||||
|
||||
This method makes a request to geonames.org.
|
||||
*/
|
@ -1,11 +1,12 @@
|
||||
/*
|
||||
* Copyright 2014, Haiku, Inc. All Rights Reserved.
|
||||
* Copyright 2014-2019, Haiku, Inc. All Rights Reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef _GEOLOCATION_H
|
||||
#define _GEOLOCATION_H
|
||||
|
||||
|
||||
#include <Country.h>
|
||||
#include <Url.h>
|
||||
|
||||
|
||||
@ -15,17 +16,24 @@ namespace BPrivate {
|
||||
class BGeolocation {
|
||||
public:
|
||||
BGeolocation();
|
||||
BGeolocation(const BUrl& service);
|
||||
BGeolocation(const BUrl& geolocationService,
|
||||
const BUrl& geocodingService);
|
||||
|
||||
status_t LocateSelf(float& latitude, float& longitude);
|
||||
status_t Locate(const BString placeName, float& latitude,
|
||||
float& longitude);
|
||||
|
||||
status_t Name(const float latitude, const float longitude,
|
||||
BString& name);
|
||||
status_t Country(const float latitude, const float longitude,
|
||||
BCountry& country);
|
||||
|
||||
private:
|
||||
BUrl fService;
|
||||
static const char* kDefaultService;
|
||||
BUrl fGeolocationService;
|
||||
BUrl fGeocodingService;
|
||||
|
||||
static const char* kDefaultGeolocationService;
|
||||
static const char* kDefaultGeocodingService;
|
||||
};
|
||||
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright 2014, Haiku, Inc. All Rights Reserved.
|
||||
* Copyright 2019, Adrien Destugues, pulkomandy@pulkomandy.tk
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
@ -19,15 +20,57 @@
|
||||
namespace BPrivate {
|
||||
|
||||
|
||||
class GeolocationListener: public BUrlProtocolListener
|
||||
{
|
||||
public:
|
||||
GeolocationListener()
|
||||
{
|
||||
pthread_cond_init(&fCompletion, NULL);
|
||||
pthread_mutex_init(&fLock, NULL);
|
||||
}
|
||||
|
||||
virtual ~GeolocationListener() {
|
||||
pthread_cond_destroy(&fCompletion);
|
||||
pthread_mutex_destroy(&fLock);
|
||||
}
|
||||
|
||||
void ConnectionOpened(BUrlRequest* caller)
|
||||
{
|
||||
pthread_mutex_lock(&fLock);
|
||||
}
|
||||
|
||||
void DataReceived(BUrlRequest*, const char* data, off_t position,
|
||||
ssize_t size) {
|
||||
fResult.WriteAt(position, data, size);
|
||||
}
|
||||
|
||||
void RequestCompleted(BUrlRequest* caller, bool success) {
|
||||
pthread_cond_signal(&fCompletion);
|
||||
pthread_mutex_unlock(&fLock);
|
||||
}
|
||||
|
||||
BMallocIO fResult;
|
||||
pthread_cond_t fCompletion;
|
||||
pthread_mutex_t fLock;
|
||||
};
|
||||
|
||||
|
||||
BGeolocation::BGeolocation()
|
||||
: fService(kDefaultService)
|
||||
: fGeolocationService(kDefaultGeolocationService),
|
||||
fGeocodingService(kDefaultGeocodingService)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
BGeolocation::BGeolocation(const BUrl& service)
|
||||
: fService(service)
|
||||
BGeolocation::BGeolocation(const BUrl& geolocationService,
|
||||
const BUrl& geocodingService)
|
||||
: fGeolocationService(geolocationService),
|
||||
fGeocodingService(geocodingService)
|
||||
{
|
||||
if (!fGeolocationService.IsValid())
|
||||
fGeolocationService.SetUrlString(kDefaultGeolocationService);
|
||||
if (!fGeocodingService.IsValid())
|
||||
fGeocodingService.SetUrlString(kDefaultGeocodingService);
|
||||
}
|
||||
|
||||
|
||||
@ -72,22 +115,11 @@ BGeolocation::LocateSelf(float& latitude, float& longitude)
|
||||
if (count < 2)
|
||||
return B_DEVICE_NOT_FOUND;
|
||||
|
||||
class GeolocationListener: public BUrlProtocolListener
|
||||
{
|
||||
public:
|
||||
virtual ~GeolocationListener() {};
|
||||
|
||||
void DataReceived(BUrlRequest*, const char* data, off_t position,
|
||||
ssize_t size) {
|
||||
result.WriteAt(position, data, size);
|
||||
}
|
||||
BMallocIO result;
|
||||
};
|
||||
|
||||
GeolocationListener listener;
|
||||
|
||||
// Send Request (POST JSON message)
|
||||
BUrlRequest* request = BUrlProtocolRoster::MakeRequest(fService, &listener);
|
||||
BUrlRequest* request = BUrlProtocolRoster::MakeRequest(fGeolocationService,
|
||||
&listener);
|
||||
if (request == NULL)
|
||||
return B_BAD_DATA;
|
||||
|
||||
@ -107,8 +139,10 @@ BGeolocation::LocateSelf(float& latitude, float& longitude)
|
||||
return result;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&listener.fLock);
|
||||
while (http->IsRunning())
|
||||
snooze(10000);
|
||||
pthread_cond_wait(&listener.fCompletion, &listener.fLock);
|
||||
pthread_mutex_unlock(&listener.fLock);
|
||||
|
||||
// Parse reply
|
||||
const BHttpResult& reply = (const BHttpResult&)http->Result();
|
||||
@ -118,7 +152,7 @@ BGeolocation::LocateSelf(float& latitude, float& longitude)
|
||||
}
|
||||
|
||||
BMessage data;
|
||||
result = BJson::Parse((char*)listener.result.Buffer(), data);
|
||||
result = BJson::Parse((char*)listener.fResult.Buffer(), data);
|
||||
delete http;
|
||||
if (result != B_OK) {
|
||||
return result;
|
||||
@ -144,6 +178,57 @@ BGeolocation::LocateSelf(float& latitude, float& longitude)
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
BGeolocation::Country(const float latitude, const float longitude,
|
||||
BCountry& country)
|
||||
{
|
||||
// Prepare the request URL
|
||||
BUrl url(fGeocodingService);
|
||||
BString requestString;
|
||||
requestString.SetToFormat("%s&lat=%f&lng=%f", url.Request().String(), latitude,
|
||||
longitude);
|
||||
url.SetPath("/countryCode");
|
||||
url.SetRequest(requestString);
|
||||
|
||||
GeolocationListener listener;
|
||||
BUrlRequest* request = BUrlProtocolRoster::MakeRequest(url,
|
||||
&listener);
|
||||
if (request == NULL)
|
||||
return B_BAD_DATA;
|
||||
|
||||
BHttpRequest* http = dynamic_cast<BHttpRequest*>(request);
|
||||
if (http == NULL) {
|
||||
delete request;
|
||||
return B_BAD_DATA;
|
||||
}
|
||||
|
||||
status_t result = http->Run();
|
||||
if (result < 0) {
|
||||
delete http;
|
||||
return result;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&listener.fLock);
|
||||
while (http->IsRunning()) {
|
||||
pthread_cond_wait(&listener.fCompletion, &listener.fLock);
|
||||
}
|
||||
pthread_mutex_unlock(&listener.fLock);
|
||||
|
||||
// Parse reply
|
||||
const BHttpResult& reply = (const BHttpResult&)http->Result();
|
||||
if (reply.StatusCode() != 200) {
|
||||
delete http;
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
off_t length = 0;
|
||||
listener.fResult.GetSize(&length);
|
||||
length -= 2; // Remove \r\n from response
|
||||
BString countryCode((char*)listener.fResult.Buffer(), (int32)length);
|
||||
return country.SetTo(countryCode);
|
||||
}
|
||||
|
||||
|
||||
#ifdef HAVE_DEFAULT_GEOLOCATION_SERVICE_KEY
|
||||
|
||||
#include "DefaultGeolocationServiceKey.h"
|
||||
@ -152,9 +237,14 @@ const char* BGeolocation::kDefaultService
|
||||
= "https://location.services.mozilla.com/v1/geolocate?key="
|
||||
DEFAULT_GEOLOCATION_SERVICE_KEY;
|
||||
|
||||
const char* BGeolocation::kDefaultGeocodingService
|
||||
= "https://secure.geonames.org/?username="
|
||||
DEFAULT_GEOCODING_SERVICE_KEY;
|
||||
|
||||
#else
|
||||
|
||||
const char* BGeolocation::kDefaultService = "";
|
||||
const char* BGeolocation::kDefaultGeolocationService = "";
|
||||
const char* BGeolocation::kDefaultGeocodingService = "";
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -24,7 +24,7 @@ void
|
||||
GeolocationTest::TestLocateSelf()
|
||||
{
|
||||
BGeolocation locator(BUrl(
|
||||
"https://location.services.mozilla.com/v1/geolocate?key=test"));
|
||||
"https://location.services.mozilla.com/v1/geolocate?key=test"), BUrl());
|
||||
float latitude, longitude;
|
||||
status_t result = locator.LocateSelf(latitude, longitude);
|
||||
|
||||
@ -33,6 +33,18 @@ GeolocationTest::TestLocateSelf()
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
GeolocationTest::TestCountry()
|
||||
{
|
||||
BGeolocation geocoder(BUrl(""), BUrl("https://secure.geonames.org/?username=demo"));
|
||||
BCountry country;
|
||||
status_t result = geocoder.Country(47.03f, 10.2f, country);
|
||||
CPPUNIT_ASSERT_EQUAL(B_OK, result);
|
||||
CPPUNIT_ASSERT_EQUAL(B_OK, country.InitCheck());
|
||||
CPPUNIT_ASSERT_EQUAL(BString("AT"), BString(country.Code()));
|
||||
}
|
||||
|
||||
|
||||
/* static */ void
|
||||
GeolocationTest::AddTests(BTestSuite& parent)
|
||||
{
|
||||
@ -40,6 +52,8 @@ GeolocationTest::AddTests(BTestSuite& parent)
|
||||
|
||||
suite.addTest(new CppUnit::TestCaller<GeolocationTest>(
|
||||
"GeolocationTest::LocateSelf", &GeolocationTest::TestLocateSelf));
|
||||
suite.addTest(new CppUnit::TestCaller<GeolocationTest>(
|
||||
"GeolocationTest::Country", &GeolocationTest::TestCountry));
|
||||
|
||||
parent.addTest("GeolocationTest", &suite);
|
||||
};
|
||||
|
@ -15,6 +15,7 @@ public:
|
||||
GeolocationTest();
|
||||
|
||||
void TestLocateSelf();
|
||||
void TestCountry();
|
||||
|
||||
static void AddTests(BTestSuite& suite);
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user