Repo created
This commit is contained in:
parent
4af19165ec
commit
68073add76
12458 changed files with 12350765 additions and 2 deletions
28
libs/platform/location_service/CMakeLists.txt
Normal file
28
libs/platform/location_service/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
project(location_service)
|
||||
|
||||
if (NOT SKIP_QT_GUI AND PLATFORM_DESKTOP AND PLATFORM_LINUX)
|
||||
message("Building with Qt Positioning")
|
||||
find_package(Qt6 REQUIRED COMPONENTS Positioning)
|
||||
set(QT_LOCATION_SERVICE true)
|
||||
elseif(NOT SKIP_QT_GUI AND PLATFORM_DESKTOP AND PLATFORM_MAC)
|
||||
set(APPLE_LOCATION_SERVICE true)
|
||||
endif()
|
||||
|
||||
set(SRC
|
||||
location_service.cpp
|
||||
location_service.hpp
|
||||
$<$<BOOL:${APPLE_LOCATION_SERVICE}>:apple_location_service.mm>
|
||||
$<$<BOOL:${QT_LOCATION_SERVICE}>:qt_location_service.hpp>
|
||||
$<$<BOOL:${QT_LOCATION_SERVICE}>:qt_location_service.cpp>
|
||||
)
|
||||
|
||||
omim_add_library(${PROJECT_NAME} ${SRC})
|
||||
|
||||
if (QT_LOCATION_SERVICE)
|
||||
target_compile_definitions(${PROJECT_NAME} PRIVATE "QT_LOCATION_SERVICE")
|
||||
target_link_libraries(${PROJECT_NAME} Qt6::Positioning)
|
||||
set_target_properties(${PROJECT_NAME} PROPERTIES AUTOMOC ON)
|
||||
elseif(APPLE_LOCATION_SERVICE)
|
||||
target_compile_definitions(${PROJECT_NAME} PRIVATE "APPLE_LOCATION_SERVICE")
|
||||
target_link_libraries(${PROJECT_NAME} -framework\ CoreLocation)
|
||||
endif()
|
||||
112
libs/platform/location_service/apple_location_service.mm
Normal file
112
libs/platform/location_service/apple_location_service.mm
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
#include "platform/location_service/location_service.hpp"
|
||||
|
||||
#include "base/logging.hpp"
|
||||
#include "base/macros.hpp"
|
||||
|
||||
#include "std/target_os.hpp"
|
||||
|
||||
#import <CoreLocation/CoreLocation.h>
|
||||
|
||||
class AppleLocationService;
|
||||
|
||||
@interface LocationManagerWrapper : NSObject <CLLocationManagerDelegate> {
|
||||
@private
|
||||
AppleLocationService * m_service;
|
||||
}
|
||||
- (id)initWithService:(AppleLocationService *) service;
|
||||
@end
|
||||
|
||||
using namespace location;
|
||||
|
||||
#define ROUGH_ACCURACY kCLLocationAccuracyNearestTenMeters
|
||||
|
||||
class AppleLocationService : public LocationService
|
||||
{
|
||||
LocationManagerWrapper * m_objCppWrapper;
|
||||
CLLocationManager * m_locationManager;
|
||||
|
||||
public:
|
||||
AppleLocationService(LocationObserver & observer) : LocationService(observer)
|
||||
{
|
||||
m_objCppWrapper = [[LocationManagerWrapper alloc] initWithService:this];
|
||||
m_locationManager = [[CLLocationManager alloc] init];
|
||||
m_locationManager.delegate = m_objCppWrapper;
|
||||
m_locationManager.desiredAccuracy = kCLLocationAccuracyBest;
|
||||
}
|
||||
|
||||
void OnLocationUpdate(GpsInfo const & info)
|
||||
{
|
||||
m_observer.OnLocationUpdated(info);
|
||||
}
|
||||
|
||||
void OnDeniedError()
|
||||
{
|
||||
m_observer.OnLocationError(location::EDenied);
|
||||
}
|
||||
|
||||
virtual void Start()
|
||||
{
|
||||
if (![CLLocationManager locationServicesEnabled])
|
||||
{
|
||||
// @TODO correctly handle situation in GUI when wifi is working and native is disabled
|
||||
//m_observer.OnLocationStatusChanged(location::ENotSupported);
|
||||
}
|
||||
else
|
||||
{
|
||||
[m_locationManager startUpdatingLocation];
|
||||
}
|
||||
}
|
||||
|
||||
virtual void Stop()
|
||||
{
|
||||
[m_locationManager stopUpdatingLocation];
|
||||
}
|
||||
};
|
||||
|
||||
@implementation LocationManagerWrapper
|
||||
|
||||
- (id)initWithService:(AppleLocationService *) service
|
||||
{
|
||||
if ((self = [super init]))
|
||||
m_service = service;
|
||||
return self;
|
||||
}
|
||||
|
||||
+ (void)location:(CLLocation *)location toGpsInfo:(GpsInfo &) info
|
||||
{
|
||||
info.m_horizontalAccuracy = location.horizontalAccuracy;
|
||||
info.m_latitude = location.coordinate.latitude;
|
||||
info.m_longitude = location.coordinate.longitude;
|
||||
info.m_timestamp = [location.timestamp timeIntervalSince1970];
|
||||
info.m_source = location::EAppleNative;
|
||||
//info.m_verticalAccuracy = location.verticalAccuracy;
|
||||
//info.m_altitude = location.altitude;
|
||||
//info.m_course = location.course;
|
||||
//info.m_speed = location.speed;
|
||||
}
|
||||
|
||||
- (void)locationManager:(CLLocationManager *)manager
|
||||
didUpdateLocations:(NSArray<CLLocation *> *)locations
|
||||
{
|
||||
UNUSED_VALUE(manager);
|
||||
GpsInfo newInfo;
|
||||
[LocationManagerWrapper location:locations.firstObject toGpsInfo:newInfo];
|
||||
m_service->OnLocationUpdate(newInfo);
|
||||
}
|
||||
|
||||
- (void)locationManager:(CLLocationManager *)manager
|
||||
didFailWithError:(NSError *)error
|
||||
{
|
||||
UNUSED_VALUE(manager);
|
||||
LOG(LWARNING, ("locationManager failed with error", error.code, error.description.UTF8String));
|
||||
|
||||
if (error.code == kCLErrorDenied)
|
||||
m_service->OnDeniedError();
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
std::unique_ptr<location::LocationService> CreateAppleLocationService(LocationObserver & observer)
|
||||
{
|
||||
return std::make_unique<AppleLocationService>(observer);
|
||||
}
|
||||
101
libs/platform/location_service/location_service.cpp
Normal file
101
libs/platform/location_service/location_service.cpp
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
#include "platform/location_service/location_service.hpp"
|
||||
|
||||
#include "std/target_os.hpp"
|
||||
|
||||
#include <ctime>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
#if defined(OMIM_OS_MAC)
|
||||
std::unique_ptr<location::LocationService> CreateAppleLocationService(location::LocationObserver &);
|
||||
#elif defined(QT_LOCATION_SERVICE)
|
||||
std::unique_ptr<location::LocationService> CreateQtLocationService(location::LocationObserver &,
|
||||
std::string const & sourceName);
|
||||
#endif
|
||||
|
||||
namespace location
|
||||
{
|
||||
static double ApproxDistanceSquareInMeters(double lat1, double lon1, double lat2, double lon2)
|
||||
{
|
||||
double const m1 = (lat1 - lat2) / 111111.;
|
||||
double const m2 = (lon1 - lon2) / 111111.;
|
||||
return m1 * m1 + m2 * m2;
|
||||
}
|
||||
|
||||
/// Chooses most accurate data from different position services
|
||||
class PositionFilter
|
||||
{
|
||||
std::optional<location::GpsInfo> m_prevLocation;
|
||||
|
||||
public:
|
||||
/// @return true if location should be sent to observers
|
||||
bool Passes(location::GpsInfo const & newLocation)
|
||||
{
|
||||
if (std::time(nullptr) - newLocation.m_timestamp > 300.0)
|
||||
return false;
|
||||
|
||||
bool passes = true;
|
||||
if (m_prevLocation)
|
||||
{
|
||||
if (newLocation.m_timestamp < m_prevLocation->m_timestamp)
|
||||
passes = false;
|
||||
else if (newLocation.m_source != m_prevLocation->m_source &&
|
||||
newLocation.m_horizontalAccuracy > m_prevLocation->m_horizontalAccuracy &&
|
||||
ApproxDistanceSquareInMeters(newLocation.m_latitude, newLocation.m_longitude, m_prevLocation->m_latitude,
|
||||
m_prevLocation->m_longitude) >
|
||||
newLocation.m_horizontalAccuracy * newLocation.m_horizontalAccuracy)
|
||||
passes = false;
|
||||
}
|
||||
else
|
||||
m_prevLocation = newLocation;
|
||||
return passes;
|
||||
}
|
||||
};
|
||||
|
||||
class DesktopLocationService
|
||||
: public LocationService
|
||||
, public LocationObserver
|
||||
{
|
||||
std::vector<std::unique_ptr<LocationService>> m_services;
|
||||
PositionFilter m_filter;
|
||||
bool m_reportFirstEvent;
|
||||
|
||||
virtual void OnLocationError(location::TLocationError errorCode) { m_observer.OnLocationError(errorCode); }
|
||||
|
||||
virtual void OnLocationUpdated(GpsInfo const & info)
|
||||
{
|
||||
if (m_filter.Passes(info))
|
||||
m_observer.OnLocationUpdated(info);
|
||||
}
|
||||
|
||||
public:
|
||||
explicit DesktopLocationService(LocationObserver & observer) : LocationService(observer), m_reportFirstEvent(true)
|
||||
{
|
||||
#if defined(QT_LOCATION_SERVICE)
|
||||
#if defined(OMIM_OS_LINUX)
|
||||
m_services.push_back(CreateQtLocationService(*this, "geoclue2"));
|
||||
#endif // OMIM_OS_LINUX
|
||||
#elif defined(APPLE_LOCATION_SERVICE) // No QT_LOCATION_SERVICE
|
||||
m_services.push_back(CreateAppleLocationService(*this));
|
||||
#endif // QT_LOCATION_SERVICE
|
||||
}
|
||||
|
||||
virtual void Start()
|
||||
{
|
||||
for (auto & service : m_services)
|
||||
service->Start();
|
||||
}
|
||||
|
||||
virtual void Stop()
|
||||
{
|
||||
for (auto & service : m_services)
|
||||
service->Stop();
|
||||
m_reportFirstEvent = true;
|
||||
}
|
||||
};
|
||||
} // namespace location
|
||||
|
||||
std::unique_ptr<location::LocationService> CreateDesktopLocationService(location::LocationObserver & observer)
|
||||
{
|
||||
return std::make_unique<location::DesktopLocationService>(observer);
|
||||
}
|
||||
33
libs/platform/location_service/location_service.hpp
Normal file
33
libs/platform/location_service/location_service.hpp
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
#pragma once
|
||||
|
||||
#include "platform/location.hpp"
|
||||
|
||||
namespace location
|
||||
{
|
||||
|
||||
class LocationObserver
|
||||
{
|
||||
public:
|
||||
virtual void OnLocationError(TLocationError errorCode) = 0;
|
||||
virtual void OnLocationUpdated(GpsInfo const & info) = 0;
|
||||
|
||||
protected:
|
||||
virtual ~LocationObserver() = default;
|
||||
};
|
||||
|
||||
class LocationService
|
||||
{
|
||||
protected:
|
||||
LocationObserver & m_observer;
|
||||
|
||||
public:
|
||||
LocationService(LocationObserver & observer) : m_observer(observer) {}
|
||||
virtual ~LocationService() = default;
|
||||
|
||||
virtual void Start() = 0;
|
||||
virtual void Stop() = 0;
|
||||
};
|
||||
|
||||
} // namespace location
|
||||
|
||||
std::unique_ptr<location::LocationService> CreateDesktopLocationService(location::LocationObserver & observer);
|
||||
162
libs/platform/location_service/qt_location_service.cpp
Normal file
162
libs/platform/location_service/qt_location_service.cpp
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
#include "platform/location_service/qt_location_service.hpp"
|
||||
|
||||
#include "base/logging.hpp"
|
||||
#include "base/macros.hpp"
|
||||
|
||||
#include "std/target_os.hpp"
|
||||
|
||||
#include <QGeoPositionInfoSource>
|
||||
|
||||
namespace
|
||||
{
|
||||
static location::GpsInfo gpsInfoFromQGeoPositionInfo(QGeoPositionInfo const & i, location::TLocationSource source)
|
||||
{
|
||||
location::GpsInfo info;
|
||||
info.m_source = source;
|
||||
|
||||
info.m_latitude = i.coordinate().latitude();
|
||||
info.m_longitude = i.coordinate().longitude();
|
||||
info.m_timestamp = static_cast<double>(i.timestamp().toSecsSinceEpoch());
|
||||
|
||||
if (i.hasAttribute(QGeoPositionInfo::HorizontalAccuracy))
|
||||
info.m_horizontalAccuracy = static_cast<double>(i.attribute(QGeoPositionInfo::HorizontalAccuracy));
|
||||
|
||||
if (i.hasAttribute(QGeoPositionInfo::VerticalAccuracy))
|
||||
info.m_verticalAccuracy = static_cast<double>(i.attribute(QGeoPositionInfo::VerticalAccuracy));
|
||||
|
||||
if (i.hasAttribute(QGeoPositionInfo::Direction))
|
||||
info.m_bearing = static_cast<double>(i.attribute(QGeoPositionInfo::Direction));
|
||||
|
||||
if (i.hasAttribute(QGeoPositionInfo::GroundSpeed))
|
||||
info.m_speed = static_cast<double>(i.attribute(QGeoPositionInfo::GroundSpeed));
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
static location::TLocationError tLocationErrorFromQGeoPositionInfoError(QGeoPositionInfoSource::Error error)
|
||||
{
|
||||
location::TLocationError result = location::TLocationError::ENotSupported;
|
||||
switch (error)
|
||||
{
|
||||
case QGeoPositionInfoSource::AccessError: result = location::TLocationError::EDenied; break;
|
||||
case QGeoPositionInfoSource::ClosedError: result = location::TLocationError::EGPSIsOff; break;
|
||||
case QGeoPositionInfoSource::NoError: result = location::TLocationError::ENoError; break;
|
||||
case QGeoPositionInfoSource::UpdateTimeoutError: result = location::TLocationError::ETimeout; break;
|
||||
case QGeoPositionInfoSource::UnknownSourceError: result = location::TLocationError::EUnknown; break;
|
||||
default: break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static location::TLocationSource qStringToTLocationSource(QString const & sourceName)
|
||||
{
|
||||
if ("geoclue2" == sourceName)
|
||||
return location::TLocationSource::EGeoClue2;
|
||||
|
||||
return location::TLocationSource::EUndefined;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
QtLocationService::QtLocationService(location::LocationObserver & observer, std::string const & sourceName)
|
||||
: LocationService(observer)
|
||||
{
|
||||
QVariantMap params;
|
||||
params["desktopId"] = "app.comaps.comaps";
|
||||
m_positionSource = QGeoPositionInfoSource::createSource(QString::fromStdString(sourceName), params, this);
|
||||
|
||||
if (!m_positionSource)
|
||||
{
|
||||
LOG(LWARNING, ("Failed to acquire QGeoPositionInfoSource from ", sourceName));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!connect(m_positionSource, &QGeoPositionInfoSource::positionUpdated, this, &QtLocationService::OnLocationUpdate))
|
||||
{
|
||||
LOG(LERROR, ("Failed to connect the signal:", "positionUpdated"));
|
||||
return;
|
||||
}
|
||||
|
||||
LOG(LDEBUG, ("Signal successfully connected:", "positionUpdated"));
|
||||
|
||||
if (!connect(m_positionSource, &QGeoPositionInfoSource::errorOccurred, this, &QtLocationService::OnErrorOccurred))
|
||||
{
|
||||
LOG(LERROR, ("Failed to connect the signal:", "errorOccurred"));
|
||||
return;
|
||||
}
|
||||
LOG(LDEBUG, ("Signal successfully connected:", "errorOccurred"));
|
||||
|
||||
if (!connect(m_positionSource, &QGeoPositionInfoSource::supportedPositioningMethodsChanged, this,
|
||||
&QtLocationService::OnSupportedPositioningMethodsChanged))
|
||||
{
|
||||
LOG(LERROR, ("Failed to connect the signal:", "supportedPositioningMethodsChanged"));
|
||||
return;
|
||||
}
|
||||
LOG(LDEBUG, ("Signal successfully connected:", "supportedPositioningMethodsChanged"));
|
||||
|
||||
m_positionSource->setUpdateInterval(1000);
|
||||
m_positionSource->setPreferredPositioningMethods(QGeoPositionInfoSource::AllPositioningMethods);
|
||||
}
|
||||
|
||||
void QtLocationService::OnLocationUpdate(QGeoPositionInfo const & info)
|
||||
{
|
||||
if (!info.isValid())
|
||||
{
|
||||
LOG(LWARNING,
|
||||
("Location update with Invalid timestamp or coordinates from:", m_positionSource->sourceName().toStdString()));
|
||||
return;
|
||||
}
|
||||
auto const & coordinate = info.coordinate();
|
||||
LOG(LDEBUG, ("Location updated with valid coordinates:", coordinate.longitude(), coordinate.latitude()));
|
||||
m_observer.OnLocationUpdated(
|
||||
gpsInfoFromQGeoPositionInfo(info, qStringToTLocationSource(m_positionSource->sourceName())));
|
||||
if (!m_clientIsActive)
|
||||
{
|
||||
m_clientIsActive = true;
|
||||
m_positionSource->startUpdates();
|
||||
}
|
||||
}
|
||||
|
||||
void QtLocationService::OnErrorOccurred(QGeoPositionInfoSource::Error positioningError)
|
||||
{
|
||||
LOG(LWARNING, ("Location error occured QGeoPositionInfoSource::Error code:", positioningError));
|
||||
m_clientIsActive = false;
|
||||
m_observer.OnLocationError(tLocationErrorFromQGeoPositionInfoError(positioningError));
|
||||
}
|
||||
|
||||
void QtLocationService::OnSupportedPositioningMethodsChanged()
|
||||
{
|
||||
auto positioningMethods = m_positionSource->supportedPositioningMethods();
|
||||
LOG(LDEBUG, ("Supported Positioning Method changed for:", m_positionSource->sourceName().toStdString(),
|
||||
"to:", positioningMethods));
|
||||
if (positioningMethods == QGeoPositionInfoSource::NoPositioningMethods)
|
||||
{
|
||||
m_clientIsActive = false;
|
||||
m_observer.OnLocationError(location::TLocationError::EGPSIsOff);
|
||||
}
|
||||
}
|
||||
|
||||
void QtLocationService::Start()
|
||||
{
|
||||
if (m_positionSource)
|
||||
{
|
||||
LOG(LDEBUG, ("Starting Updates from:", m_positionSource->sourceName().toStdString()));
|
||||
// Request the first update with a timeout to 30 minutes which is needed on devices that don't make use of `A-GNSS`
|
||||
// and can't get a lock within Qt's default `UPDATE_TIMEOUT_COLDSTART` (currently 2 minutes).
|
||||
m_positionSource->requestUpdate(1800000);
|
||||
}
|
||||
}
|
||||
|
||||
void QtLocationService::Stop()
|
||||
{
|
||||
if (m_positionSource && m_clientIsActive)
|
||||
{
|
||||
LOG(LDEBUG, ("Stopping Updates from:", m_positionSource->sourceName().toStdString()));
|
||||
m_positionSource->stopUpdates();
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<location::LocationService> CreateQtLocationService(location::LocationObserver & observer,
|
||||
std::string const & sourceName)
|
||||
{
|
||||
return std::make_unique<QtLocationService>(observer, sourceName);
|
||||
}
|
||||
30
libs/platform/location_service/qt_location_service.hpp
Normal file
30
libs/platform/location_service/qt_location_service.hpp
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
#pragma once
|
||||
|
||||
#include "platform/location_service/location_service.hpp"
|
||||
|
||||
#include <QGeoPositionInfoSource>
|
||||
|
||||
class QtLocationService
|
||||
: public QObject
|
||||
, public location::LocationService
|
||||
{
|
||||
Q_OBJECT
|
||||
QGeoPositionInfoSource * m_positionSource;
|
||||
// Unfortunately when the source is `geoclue2`
|
||||
// we would need access to the `Active` D-Bus property
|
||||
// https://www.freedesktop.org/software/geoclue/docs
|
||||
// /gdbus-org.freedesktop.GeoClue2.Client.html#gdbus-property-org-freedesktop-GeoClue2-Client.Active
|
||||
// But `QGeoPositionInfoSource` doesn't expose that so we have to deduce its state.
|
||||
bool m_clientIsActive = false;
|
||||
|
||||
public:
|
||||
explicit QtLocationService(location::LocationObserver &, std::string const &);
|
||||
virtual ~QtLocationService() {}
|
||||
virtual void Start();
|
||||
virtual void Stop();
|
||||
|
||||
public slots:
|
||||
void OnLocationUpdate(QGeoPositionInfo const &);
|
||||
void OnErrorOccurred(QGeoPositionInfoSource::Error);
|
||||
void OnSupportedPositioningMethodsChanged();
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue