Add BGeolocation experimental API.
A BGeolocation object can query an online service to get geolocation and geotagging data: * LocateSelf() tries to locate the machine it is running on, by using an online database of wifi access points * Locate() (not yet implemented) searches a BString and converts it to lat/lon coordinates (reverse geotagging) * Name() (not yet implemented) finds a suitable name for the given coordinates (address, building name, or anything fitting). The default service used is openbmap.org, which is freely available but not very accurate. A request has been sent to Mozilla to use MLS (Mozilla Location Services), which is a bit more accurate but needs an API key. MLS is used for geolocation on FirefoxOS, for mobile phones which don't have a GPS, and the data can be contributed by Firefox for Android or the dedicated MozStumbler app. Alternatively, Google Maps also provide the service, but wants people to pay for it. Google Maps data is more accurate as all Android devices contribute data to it. All 3 services use the same JSON-based API: we send a list of reachable Wifi APs (mac address and signal strength), and we get lattitude and longitude information, and possibly extra data which will currently be unused. This can be used to implement HTML5 geolocation with reasonably accurate results, but it can also be used in other places. For example FirstBootPrompt could try to guess a list of most likely languages and keyboard layouts from it (if wifi is working at install time, that is).
This commit is contained in:
parent
d3c865ecd8
commit
13f0458896
29
headers/private/shared/Geolocation.h
Normal file
29
headers/private/shared/Geolocation.h
Normal file
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright 2014, Haiku, Inc. All Rights Reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef _GEOLOCATION_H
|
||||
#define _GEOLOCATION_H
|
||||
|
||||
|
||||
#include <Url.h>
|
||||
|
||||
|
||||
class BGeolocation {
|
||||
public:
|
||||
BGeolocation();
|
||||
BGeolocation(const BUrl& service);
|
||||
|
||||
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);
|
||||
|
||||
private:
|
||||
BUrl fService;
|
||||
static const BUrl kDefaultService;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
139
src/kits/network/libnetapi/Geolocation.cpp
Normal file
139
src/kits/network/libnetapi/Geolocation.cpp
Normal file
@ -0,0 +1,139 @@
|
||||
/*
|
||||
* Copyright 2014, Haiku, Inc. All Rights Reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
|
||||
#include <Geolocation.h>
|
||||
|
||||
#include <HttpRequest.h>
|
||||
#include <Json.h>
|
||||
#include <NetworkDevice.h>
|
||||
#include <NetworkInterface.h>
|
||||
#include <NetworkRoster.h>
|
||||
#include <String.h>
|
||||
#include <UrlProtocolRoster.h>
|
||||
#include <UrlRequest.h>
|
||||
|
||||
|
||||
BGeolocation::BGeolocation()
|
||||
: fService(kDefaultService)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
BGeolocation::BGeolocation(const BUrl& service)
|
||||
: fService(service)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
BGeolocation::LocateSelf(float& latitude, float& longitude)
|
||||
{
|
||||
// Enumerate wifi network and build JSON message
|
||||
BNetworkRoster& roster = BNetworkRoster::Default();
|
||||
uint32 interfaceCookie = 0;
|
||||
BNetworkInterface interface;
|
||||
|
||||
BString query("{\"wifiAccessPoints\":[");
|
||||
int32 count = 0;
|
||||
|
||||
while (roster.GetNextInterface(&interfaceCookie, interface) == B_OK) {
|
||||
uint32 networkCookie = 0;
|
||||
wireless_network network;
|
||||
|
||||
BNetworkDevice device(interface.Name());
|
||||
// TODO is that the correct way to enumerate devices?
|
||||
|
||||
while (device.GetNextNetwork(networkCookie, network) == B_OK) {
|
||||
if (count != 0)
|
||||
query += ';';
|
||||
|
||||
count++;
|
||||
|
||||
query += "\n{\"macAddress\":\"";
|
||||
query += network.address.ToString().ToUpper();
|
||||
query += "\", \"signalStrength\":";
|
||||
query << (int)network.signal_strength;
|
||||
query += ", \"signalToNoiseRatio\":";
|
||||
query << (int)network.noise_level;
|
||||
query += '}';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
query += "]}";
|
||||
|
||||
// Check that we have enough data (we need at least 2 networks)
|
||||
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);
|
||||
if (request == NULL)
|
||||
return B_BAD_DATA;
|
||||
|
||||
BHttpRequest* http = dynamic_cast<BHttpRequest*>(request);
|
||||
if (http == NULL) {
|
||||
delete request;
|
||||
return B_BAD_DATA;
|
||||
}
|
||||
|
||||
BMemoryIO* io = new BMemoryIO(query.String(), query.Length());
|
||||
http->AdoptInputData(io, query.Length());
|
||||
|
||||
status_t result = http->Run();
|
||||
if (result < 0) {
|
||||
delete http;
|
||||
return result;
|
||||
}
|
||||
|
||||
while (http->IsRunning())
|
||||
snooze(10000);
|
||||
|
||||
// Parse reply
|
||||
const BHttpResult& reply = (const BHttpResult&)http->Result();
|
||||
if (reply.StatusCode() != 200) {
|
||||
delete http;
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
BMessage data;
|
||||
result = BJson::Parse(data, (char*)listener.result.Buffer());
|
||||
delete http;
|
||||
if (result != B_OK) {
|
||||
return result;
|
||||
}
|
||||
|
||||
BMessage location;
|
||||
result = data.FindMessage("location", &location);
|
||||
if(result != B_OK)
|
||||
return result;
|
||||
|
||||
BString lat = location.FindString("latitude");
|
||||
BString lon = location.FindString("longitude");
|
||||
|
||||
latitude = strtof(lat.String(), NULL);
|
||||
longitude = strtof(lon.String(), NULL);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
const BUrl BGeolocation::kDefaultService = BUrl(
|
||||
"http://openbmap.org/api/json.php5");
|
@ -56,6 +56,14 @@ for architectureObject in [ MultiArchSubDirSetup ] {
|
||||
Socket.cpp
|
||||
SecureSocket.cpp
|
||||
|
||||
# TODO: another add-on for file:// (a much simpler one)
|
||||
FileRequest.cpp
|
||||
|
||||
Geolocation.cpp
|
||||
|
||||
# TODO: another add-on for gopher://
|
||||
GopherRequest.cpp
|
||||
|
||||
# TODO: The HTTP stuff should all go into an add-on. It needs
|
||||
# linking against libcrypto.so and only the add-on should link
|
||||
# against it.
|
||||
@ -67,12 +75,6 @@ for architectureObject in [ MultiArchSubDirSetup ] {
|
||||
HttpResult.cpp
|
||||
HttpTime.cpp
|
||||
|
||||
# TODO: another add-on for file:// (a much simpler one)
|
||||
FileRequest.cpp
|
||||
|
||||
# TODO: another add-on for gopher://
|
||||
GopherRequest.cpp
|
||||
|
||||
notifications.cpp
|
||||
|
||||
$(md5Sources)
|
||||
|
41
src/tests/kits/net/service/GeolocationTest.cpp
Normal file
41
src/tests/kits/net/service/GeolocationTest.cpp
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2014, Haiku, inc.
|
||||
* Distributed under the terms of the MIT licence
|
||||
*/
|
||||
|
||||
|
||||
#include "GeolocationTest.h"
|
||||
|
||||
#include <Geolocation.h>
|
||||
|
||||
#include <cppunit/TestCaller.h>
|
||||
#include <cppunit/TestSuite.h>
|
||||
|
||||
|
||||
GeolocationTest::GeolocationTest()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
GeolocationTest::TestLocateSelf()
|
||||
{
|
||||
BGeolocation locator;
|
||||
float latitude, longitude;
|
||||
status_t result = locator.LocateSelf(latitude, longitude);
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(B_OK, result);
|
||||
printf("Your position is: %f %f\n", latitude, longitude);
|
||||
}
|
||||
|
||||
|
||||
/* static */ void
|
||||
GeolocationTest::AddTests(BTestSuite& parent)
|
||||
{
|
||||
CppUnit::TestSuite& suite = *new CppUnit::TestSuite("GeolocationTest");
|
||||
|
||||
suite.addTest(new CppUnit::TestCaller<GeolocationTest>(
|
||||
"GeolocationTest::LocateSelf", &GeolocationTest::TestLocateSelf));
|
||||
|
||||
parent.addTest("GeolocationTest", &suite);
|
||||
};
|
23
src/tests/kits/net/service/GeolocationTest.h
Normal file
23
src/tests/kits/net/service/GeolocationTest.h
Normal file
@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright 2014 Haiku, inc.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef GEOLOCATION_TEST_H
|
||||
#define GEOLOCAITON_TEST_H
|
||||
|
||||
|
||||
#include <TestCase.h>
|
||||
#include <TestSuite.h>
|
||||
|
||||
|
||||
class GeolocationTest: public BTestCase {
|
||||
public:
|
||||
GeolocationTest();
|
||||
|
||||
void TestLocateSelf();
|
||||
|
||||
static void AddTests(BTestSuite& suite);
|
||||
};
|
||||
|
||||
|
||||
#endif
|
@ -1,10 +1,13 @@
|
||||
SubDir HAIKU_TOP src tests kits net service ;
|
||||
|
||||
UsePrivateHeaders private shared ;
|
||||
|
||||
UnitTestLib servicekittest.so :
|
||||
ServiceKitTestAddon.cpp
|
||||
|
||||
CookieTest.cpp
|
||||
DataTest.cpp
|
||||
GeolocationTest.cpp
|
||||
HttpTest.cpp
|
||||
UrlTest.cpp
|
||||
|
||||
|
@ -9,6 +9,7 @@
|
||||
|
||||
#include "CookieTest.h"
|
||||
#include "DataTest.h"
|
||||
#include "GeolocationTest.h"
|
||||
#include "HttpTest.h"
|
||||
#include "UrlTest.h"
|
||||
|
||||
@ -22,6 +23,7 @@ getTestSuite()
|
||||
UrlTest::AddTests(*suite);
|
||||
HttpTest::AddTests(*suite);
|
||||
DataTest::AddTests(*suite);
|
||||
GeolocationTest::AddTests(*suite);
|
||||
|
||||
return suite;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user