Use a vector database to get the timezone based on territorial boundaries

This commit is contained in:
Erik Hofman 2021-03-11 15:59:52 +01:00
parent bbd84a944a
commit c9e24dcb0b
7 changed files with 1426 additions and 17 deletions

View File

@ -6,6 +6,7 @@ set(HEADERS
sg_time.hxx sg_time.hxx
timestamp.hxx timestamp.hxx
timezone.h timezone.h
zonedetect.h
lowleveltime.h lowleveltime.h
) )
@ -14,6 +15,7 @@ set(SOURCES
sg_time.cxx sg_time.cxx
timestamp.cxx timestamp.cxx
timezone.cxx timezone.cxx
zonedetect.c
) )
simgear_component(timing timing "${SOURCES}" "${HEADERS}") simgear_component(timing timing "${SOURCES}" "${HEADERS}")

9
simgear/timing/README Normal file
View File

@ -0,0 +1,9 @@
ZoneDetect: BSD-3-Clause License
https://github.com/BertoldVdb/ZoneDetect
This is a C library that allows you to find an area a point belongs to using a
database file. A typical example would be looking up the country or timezone
given a latitude and longitude. The timezone database also contains the country information.
download the database files:
https://cdn.bertold.org/zonedetect/db/db.zip

View File

@ -84,7 +84,7 @@ void SGTime::init( const SGGeod& location, const SGPath& root, time_t init_time
if ( !root.isNull()) { if ( !root.isNull()) {
if (!static_tzContainer.get()) { if (!static_tzContainer.get()) {
SGPath zone( root ); SGPath zone( root );
zone.append( "zone.tab" ); zone.append( "timezone16.bin" );
SG_LOG( SG_EVENT, SG_INFO, "Reading timezone info from: " << zone ); SG_LOG( SG_EVENT, SG_INFO, "Reading timezone info from: " << zone );
std::string zs = zone.utf8Str(); std::string zs = zone.utf8Str();
static_tzContainer.reset(new SGTimeZoneContainer( zs.c_str() )); static_tzContainer.reset(new SGTimeZoneContainer( zs.c_str() ));

View File

@ -38,6 +38,7 @@
#include <simgear/structure/exception.hxx> #include <simgear/structure/exception.hxx>
#include <simgear/misc/strutils.hxx> #include <simgear/misc/strutils.hxx>
#include "zonedetect.h"
#include "timezone.h" #include "timezone.h"
SGTimeZone::SGTimeZone(const SGGeod& geod, char* cc, char* desc) : SGTimeZone::SGTimeZone(const SGGeod& geod, char* cc, char* desc) :
@ -133,14 +134,17 @@ SGTimeZone::SGTimeZone(const SGTimeZone& other)
/********* Member functions for SGTimeZoneContainer class ********/ /********* Member functions for SGTimeZoneContainer class ********/
SGTimeZoneContainer::SGTimeZoneContainer(const char *filename) SGTimeZoneContainer::SGTimeZoneContainer(const char *filename) :
tzdb_file(filename)
{ {
if (tzdb_file.find("zone.tab") != std::string::npos)
{
char buffer[256]; char buffer[256];
#if defined(SG_WINDOWS) #if defined(SG_WINDOWS)
const std::wstring wfile = simgear::strutils::convertUtf8ToWString(filename); const std::wstring wfile = simgear::strutils::convertUtf8ToWString(filename);
FILE* infile = _wfopen(wfile.c_str(), L"rb"); FILE* infile = _wfopen(wfile.c_str(), L"rb");
#else #else
FILE* infile = fopen(filename, "rb"); FILE* infile = fopen(filename, "rb");
#endif #endif
if (!(infile)) { if (!(infile)) {
std::string e = "Unable to open time zone file '"; std::string e = "Unable to open time zone file '";
@ -171,28 +175,76 @@ SGTimeZoneContainer::SGTimeZoneContainer(const char *filename)
} }
fclose(infile); fclose(infile);
is_zone_tab = true;
}
} }
SGTimeZoneContainer::~SGTimeZoneContainer() SGTimeZoneContainer::~SGTimeZoneContainer()
{ {
TZVec::iterator it = zones.begin(); if (is_zone_tab)
for (; it != zones.end(); ++it) { {
delete *it; TZVec::iterator it = zones.begin();
for (; it != zones.end(); ++it) {
delete *it;
}
} }
} }
SGTimeZone* SGTimeZoneContainer::getNearest(const SGGeod& ref) const SGTimeZone* SGTimeZoneContainer::getNearest(const SGGeod& ref) const
{ {
SGVec3d refCart(SGVec3d::fromGeod(ref));
SGTimeZone* match = NULL; SGTimeZone* match = NULL;
double minDist2 = HUGE_VAL;
TZVec::const_iterator it = zones.begin(); if (is_zone_tab)
for (; it != zones.end(); ++it) { {
double d2 = distSqr((*it)->cartCenterpoint(), refCart); SGVec3d refCart(SGVec3d::fromGeod(ref));
if (d2 < minDist2) { double minDist2 = HUGE_VAL;
match = *it;
minDist2 = d2; TZVec::const_iterator it = zones.begin();
for (; it != zones.end(); ++it) {
double d2 = distSqr((*it)->cartCenterpoint(), refCart);
if (d2 < minDist2) {
match = *it;
minDist2 = d2;
}
}
}
else if (!tzdb_file.empty())
{
ZoneDetect *const cd = ZDOpenDatabase(tzdb_file.c_str());
if (cd)
{
char *CountryAlpha2 = nullptr;
char *TimezoneIdPrefix = nullptr;
char *TimezoneId = nullptr;
float safezone = 0;
float lat = ref.getLatitudeDeg();
float lon = ref.getLongitudeDeg();
ZoneDetectResult *results = ZDLookup(cd, lat, lon, &safezone);
if (results && results[0].data)
{
for(unsigned i=0; i<results[0].numFields; ++i)
{
if(results[0].fieldNames[i] && results[0].data[i])
{
std::string fieldName = results[0].fieldNames[i];
if (fieldName == "CountryAlpha2") {
CountryAlpha2 = results[0].data[i];
} else if (fieldName == "TimezoneIdPrefix") {
TimezoneIdPrefix = results[0].data[i];
} else if (fieldName == "TimezoneId") {
TimezoneId = results[0].data[i];
}
}
}
std::string desc;
desc = TimezoneIdPrefix;
desc += TimezoneId;
match = new SGTimeZone(ref, CountryAlpha2, (char*)desc.c_str());
}
ZDCloseDatabase(cd);
} }
} }

View File

@ -97,6 +97,10 @@ public:
SGTimeZone* getNearest(const SGGeod& ref) const; SGTimeZone* getNearest(const SGGeod& ref) const;
private: private:
std::string tzdb_file;
// zone.tab related
bool is_zone_tab = false;
typedef std::vector<SGTimeZone*> TZVec; typedef std::vector<SGTimeZone*> TZVec;
TZVec zones; TZVec zones;
}; };

1251
simgear/timing/zonedetect.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,91 @@
/*
* Copyright (c) 2018, Bertold Van den Bergh (vandenbergh@bertold.org)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the author nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR DISTRIBUTOR BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdint.h>
#ifndef INCL_ZONEDETECT_H_
#define INCL_ZONEDETECT_H_
#if !defined(ZD_EXPORT)
#if defined(_MSC_VER)
#define ZD_EXPORT __declspec(dllimport)
#else
#define ZD_EXPORT
#endif
#endif
typedef enum {
ZD_LOOKUP_IGNORE = -3,
ZD_LOOKUP_END = -2,
ZD_LOOKUP_PARSE_ERROR = -1,
ZD_LOOKUP_NOT_IN_ZONE = 0,
ZD_LOOKUP_IN_ZONE = 1,
ZD_LOOKUP_IN_EXCLUDED_ZONE = 2,
ZD_LOOKUP_ON_BORDER_VERTEX = 3,
ZD_LOOKUP_ON_BORDER_SEGMENT = 4
} ZDLookupResult;
typedef struct {
ZDLookupResult lookupResult;
uint32_t polygonId;
uint32_t metaId;
uint8_t numFields;
char **fieldNames;
char **data;
} ZoneDetectResult;
struct ZoneDetectOpaque;
typedef struct ZoneDetectOpaque ZoneDetect;
#ifdef __cplusplus
extern "C" {
#endif
ZD_EXPORT ZoneDetect *ZDOpenDatabase(const char *path);
ZD_EXPORT ZoneDetect *ZDOpenDatabaseFromMemory(void* buffer, size_t length);
ZD_EXPORT void ZDCloseDatabase(ZoneDetect *library);
ZD_EXPORT ZoneDetectResult *ZDLookup(const ZoneDetect *library, float lat, float lon, float *safezone);
ZD_EXPORT void ZDFreeResults(ZoneDetectResult *results);
ZD_EXPORT const char *ZDGetNotice(const ZoneDetect *library);
ZD_EXPORT uint8_t ZDGetTableType(const ZoneDetect *library);
ZD_EXPORT const char *ZDLookupResultToString(ZDLookupResult result);
ZD_EXPORT int ZDSetErrorHandler(void (*handler)(int, int));
ZD_EXPORT const char *ZDGetErrorString(int errZD);
ZD_EXPORT float* ZDPolygonToList(const ZoneDetect *library, uint32_t polygonId, size_t* length);
ZD_EXPORT char* ZDHelperSimpleLookupString(const ZoneDetect* library, float lat, float lon);
#ifdef __cplusplus
}
#endif
#endif // INCL_ZONEDETECT_H_