Upload database generation code (not very clean...)

This commit is contained in:
Bertold Van den Bergh 2019-01-10 21:03:08 +01:00
parent fffafa15cc
commit 3629e97f9d
8 changed files with 1015 additions and 0 deletions

3
database/README.md Normal file
View file

@ -0,0 +1,3 @@
# Database files
Please download the database files [here](https://files.bertold.org/zd/db.zip) or create them using the builder.

View file

@ -0,0 +1,566 @@
/*
* 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 <shapefil.h>
#include <iostream>
#include <limits>
#include <fstream>
#include <vector>
#include <algorithm>
#include <unordered_map>
#include <functional>
#include <math.h>
#include <tuple>
const double Inf = std::numeric_limits<float>::infinity();
std::unordered_map<std::string, std::string> alpha2ToName;
std::unordered_map<std::string, std::string> tzidToAlpha2;
void errorFatal(std::string what)
{
std::cerr<<what<<"\n";
exit(1);
}
int encodeVariableLength(std::vector<uint8_t>& output, int64_t valueIn, bool handleNeg = true)
{
uint64_t value = valueIn * 2;
if(valueIn < 0) {
value = -valueIn * 2 + 1;
}
if(!handleNeg) {
value = valueIn;
}
int bytesUsed = 0;
do {
uint8_t byteOut = value & 0x7F;
if(value >= 128) {
byteOut |= 0x80;
}
output.push_back(byteOut);
bytesUsed ++;
value >>= 7;
} while(value);
return bytesUsed;
}
int64_t doubleToFixedPoint(double input, double scale, unsigned int precision = 32)
{
double inputScaled = input / scale;
return inputScaled * pow(2, precision-1);
}
struct Point {
Point(double lat = 0, double lon = 0)
{
lat_ = lat;
lon_ = lon;
}
Point operator-(const Point& p)
{
Point result(lat_ - p.lat_, lon_ - p.lon_);
return result;
}
std::tuple<int64_t, int64_t> toFixedPoint(unsigned int precision = 32)
{
int64_t latFixedPoint = doubleToFixedPoint(lat_, 90, precision);
int64_t lonFixedPoint = doubleToFixedPoint(lon_, 180, precision);
return std::make_tuple(latFixedPoint, lonFixedPoint);
}
int encodePointBinary(std::vector<uint8_t>& output, unsigned int precision = 32)
{
int64_t latFixedPoint, lonFixedPoint;
std::tie(latFixedPoint, lonFixedPoint) = toFixedPoint(precision);
int bytesUsed = encodeVariableLength(output, latFixedPoint);
bytesUsed += encodeVariableLength(output, lonFixedPoint);
return bytesUsed;
}
double lat_;
double lon_;
};
struct PolygonData {
Point boundingMin;
Point boundingMax;
std::vector<Point> points_;
unsigned long fileIndex_ = 0;
unsigned long metadataId_;
void processPoint(const Point& p)
{
if(p.lat_ < boundingMin.lat_) {
boundingMin.lat_ = p.lat_;
}
if(p.lon_ < boundingMin.lon_) {
boundingMin.lon_ = p.lon_;
}
if(p.lat_ > boundingMax.lat_) {
boundingMax.lat_ = p.lat_;
}
if(p.lon_ > boundingMax.lon_) {
boundingMax.lon_ = p.lon_;
}
points_.push_back(p);
}
PolygonData(unsigned long id):
boundingMin(Inf, Inf),
boundingMax(-Inf, -Inf),
metadataId_(id)
{
}
long encodeBinaryData(std::vector<uint8_t>& output, unsigned int precision = 20)
{
long bytesEncoded = 0;
bool first = true;
int64_t latFixedPoint = 0, lonFixedPoint = 0;
int64_t latFixedPointPrev, lonFixedPointPrev;
uint64_t vertices = 0;
std::vector<uint8_t> tmp;
int64_t diffLatAcc = 0, diffLonAcc = 0, diffLatPrev = 0, diffLonPrev = 0;
for(Point& point: points_) {
/* The points should first be rounded, and then the integer value is differentiated */
latFixedPointPrev = latFixedPoint;
lonFixedPointPrev = lonFixedPoint;
std::tie(latFixedPoint, lonFixedPoint) = point.toFixedPoint(precision);
int64_t diffLat = latFixedPoint - latFixedPointPrev;
int64_t diffLon = lonFixedPoint - lonFixedPointPrev;
if(first) {
/* First point is always encoded */
vertices++;
encodeVariableLength(tmp, latFixedPoint);
encodeVariableLength(tmp, lonFixedPoint);
first = false;
} else {
/* Ignore points that are not different */
if(!diffLon && !diffLat) {
continue;
}
if(diffLat != diffLatPrev || diffLon != diffLonPrev) {
/* Encode accumulator */
vertices++;
encodeVariableLength(tmp, diffLatAcc);
encodeVariableLength(tmp, diffLonAcc);
diffLatAcc = 0;
diffLonAcc = 0;
}
diffLatAcc += diffLat;
diffLonAcc += diffLon;
}
diffLatPrev = diffLat;
diffLonPrev = diffLon;
}
/* Encode final point */
vertices++;
encodeVariableLength(tmp, diffLatAcc);
encodeVariableLength(tmp, diffLonAcc);
encodeVariableLength(output, vertices, false);
std::copy(tmp.begin(), tmp.end(), std::back_inserter(output));
return bytesEncoded;
}
};
void encodeStringToBinary(std::vector<uint8_t>& output, std::string& input)
{
encodeVariableLength(output, input.size(), false);
for(unsigned int i=0; i<input.size(); i++) {
output.push_back(input[i] ^ 0x80);
}
}
std::unordered_map<std::string, uint64_t> usedStrings_;
struct MetaData {
void encodeBinaryData(std::vector<uint8_t>& output)
{
for(std::string& str: data_) {
if(str.length() >= 256) {
std::cout << "Metadata string is too long\n";
exit(1);
}
if(!usedStrings_.count(str)) {
usedStrings_[str] = output.size();
encodeStringToBinary(output, str);
} else {
encodeVariableLength(output, usedStrings_[str] + 256, false);
}
}
}
std::vector<std::string> data_;
unsigned long fileIndex_;
};
std::vector<PolygonData*> polygons_;
std::vector<MetaData> metadata_;
std::vector<std::string> fieldNames_;
unsigned int decodeVariableLength(uint8_t* buffer, int64_t* result, bool handleNeg = true)
{
int64_t value = 0;
unsigned int i=0, shift = 0;
do {
value |= (buffer[i] & 0x7F) << shift;
shift += 7;
} while(buffer[i++] & 0x80);
if(!handleNeg) {
*result = value;
} else {
*result = (value & 1)?-(value/2):(value/2);
}
return i;
}
void readMetaDataTimezone(DBFHandle dataHandle)
{
/* Specify field names */
fieldNames_.push_back("TimezoneIdPrefix");
fieldNames_.push_back("TimezoneId");
fieldNames_.push_back("CountryAlpha2");
fieldNames_.push_back("CountryName");
/* Parse attribute names */
for(int i = 0; i < DBFGetRecordCount(dataHandle); i++) {
metadata_[i].data_.resize(4);
for(int j = 0; j < DBFGetFieldCount(dataHandle); j++) {
char fieldTitle[12];
int fieldWidth, fieldDecimals;
DBFFieldType eType = DBFGetFieldInfo(dataHandle, j, fieldTitle, &fieldWidth, &fieldDecimals);
fieldTitle[11] = 0;
std::string fieldTitleStr(fieldTitle);
if( eType == FTString ) {
if(fieldTitleStr == "tzid") {
std::string data = DBFReadStringAttribute(dataHandle, i, j);
size_t pos = data.find('/');
if (pos == std::string::npos) {
metadata_[i].data_.at(0) = data;
} else {
metadata_[i].data_.at(0) = data.substr(0, pos) + "/";
metadata_[i].data_.at(1) = data.substr(pos + 1, std::string::npos);
}
if(tzidToAlpha2.count(data)) {
metadata_[i].data_.at(2) = tzidToAlpha2[data];
if(alpha2ToName.count(metadata_[i].data_.at(2))) {
metadata_[i].data_.at(3) = alpha2ToName[metadata_[i].data_.at(2)];
} else {
std::cout<<metadata_[i].data_.at(2)<< " not found in alpha2ToName! ("<<data<<")\n";
}
} else {
std::cout<<data<<" not found in zoneToAlpha2!\n";
}
}
}
}
}
}
void readMetaDataNaturalEarthCountry(DBFHandle dataHandle)
{
/* Specify field names */
fieldNames_.push_back("Alpha2");
fieldNames_.push_back("Alpha3");
fieldNames_.push_back("Name");
/* Parse attribute names */
for(int i = 0; i < DBFGetRecordCount(dataHandle); i++) {
metadata_[i].data_.resize(3);
for(int j = 0; j < DBFGetFieldCount(dataHandle); j++) {
char fieldTitle[12];
int fieldWidth, fieldDecimals;
DBFFieldType eType = DBFGetFieldInfo(dataHandle, j, fieldTitle, &fieldWidth, &fieldDecimals);
fieldTitle[11] = 0;
std::string fieldTitleStr(fieldTitle);
if( eType == FTString ) {
if(fieldTitleStr == "ISO_A2" || fieldTitleStr == "WB_A2") {
std::string tmp = DBFReadStringAttribute(dataHandle, i, j);
if(tmp != "-99") {
metadata_[i].data_.at(0) = tmp;
}
} else if(fieldTitleStr == "ISO_A3" || fieldTitleStr == "WB_A3" || fieldTitleStr == "BRK_A3") {
std::string tmp = DBFReadStringAttribute(dataHandle, i, j);
if(tmp != "-99") {
metadata_[i].data_.at(1) = tmp;
}
} else if(fieldTitleStr == "NAME_LONG") {
metadata_[i].data_.at(2) = DBFReadStringAttribute(dataHandle, i, j);
}
}
}
}
}
std::unordered_map<std::string, std::string> parseAlpha2ToName(DBFHandle dataHandle)
{
std::unordered_map<std::string, std::string> result;
for(int i = 0; i < DBFGetRecordCount(dataHandle); i++) {
std::string alpha2, name;
for(int j = 0; j < DBFGetFieldCount(dataHandle); j++) {
char fieldTitle[12];
int fieldWidth, fieldDecimals;
DBFFieldType eType = DBFGetFieldInfo(dataHandle, j, fieldTitle, &fieldWidth, &fieldDecimals);
fieldTitle[11] = 0;
std::string fieldTitleStr(fieldTitle);
if( eType == FTString ) {
if(fieldTitleStr == "ISO_A2" || fieldTitleStr == "WB_A2") {
std::string tmp = DBFReadStringAttribute(dataHandle, i, j);
if(tmp != "-99" && alpha2 == "") {
alpha2 = tmp;
}
} else if(fieldTitleStr == "NAME_LONG") {
name = DBFReadStringAttribute(dataHandle, i, j);
}
}
}
if(alpha2 != "") {
result[alpha2]=name;
}
}
result["GF"]="French Guiana";
result["GP"]="Guadeloupe";
result["BQ"]="Bonaire";
result["MQ"]="Martinique";
result["SJ"]="Svalbard and Jan Mayen Islands";
result["NO"]="Norway";
result["CX"]="Christmas Island";
result["CC"]="Cocos Islands";
result["YT"]="Mayotte";
result["RE"]="Réunion";
result["TK"]="Tokelau";
return result;
}
std::unordered_map<std::string, std::string> parseTimezoneToAlpha2(std::string path)
{
std::unordered_map<std::string, std::string> result;
//TODO: Clean solution...
#include "zoneToAlpha.h"
return result;
}
int main(int argc, char ** argv )
{
if(argc != 6) {
std::cout << "Wrong number of parameters\n";
return 1;
}
tzidToAlpha2 = parseTimezoneToAlpha2("TODO");
char tableType = argv[1][0];
std::string path = argv[2];
std::string outPath = argv[3];
unsigned int precision = strtol(argv[4], NULL, 10);
std::string notice = argv[5];
DBFHandle dataHandle = DBFOpen("naturalearth/ne_10m_admin_0_countries_lakes", "rb" );
alpha2ToName = parseAlpha2ToName(dataHandle);
DBFClose(dataHandle);
dataHandle = DBFOpen(path.c_str(), "rb" );
if( dataHandle == NULL ) {
errorFatal("Could not open attribute file\n");
}
metadata_.resize(DBFGetRecordCount(dataHandle));
std::cout << "Reading "<<metadata_.size()<<" metadata records.\n";
if(tableType == 'C') {
readMetaDataNaturalEarthCountry(dataHandle);
} else if(tableType == 'T') {
readMetaDataTimezone(dataHandle);
} else {
std::cout << "Unknown table type\n";
return 1;
}
DBFClose(dataHandle);
SHPHandle shapeHandle = SHPOpen(path.c_str(), "rb");
if( shapeHandle == NULL ) {
errorFatal("Could not open shapefile\n");
}
int numEntities, shapeType, totalPolygons = 0;
SHPGetInfo(shapeHandle, &numEntities, &shapeType, NULL, NULL);
std::cout<<"Opened "<<SHPTypeName( shapeType )<< " file with "<<numEntities<<" entries.\n";
for(int i = 0; i < numEntities; i++ ) {
SHPObject *shapeObject;
shapeObject = SHPReadObject( shapeHandle, i );
if(shapeObject) {
if(shapeObject->nSHPType != 3 && shapeObject->nSHPType != 5 &&
shapeObject->nSHPType != 13 && shapeObject->nSHPType != 15) {
std::cout<<"Unsupported shape object ("<< SHPTypeName(shapeObject->nSHPType) <<")\n";
continue;
}
int partIndex = 0;
PolygonData* polygonData = nullptr;
for(int j = 0; j < shapeObject->nVertices; j++ ) {
if(j == 0 || j == shapeObject->panPartStart[partIndex]) {
totalPolygons++;
if(polygonData) {
/* Commit it */
polygons_.push_back(polygonData);
}
polygonData = new PolygonData(i);
if(partIndex + 1 < shapeObject->nParts) {
partIndex++;
}
}
Point p(shapeObject->padfY[j], shapeObject->padfX[j]);
polygonData->processPoint(p);
}
if(polygonData) {
/* Commit it */
polygons_.push_back(polygonData);
}
SHPDestroyObject(shapeObject);
}
}
SHPClose(shapeHandle);
std::cout<<"Parsed "<<totalPolygons<<" polygons.\n";
/* Sort according to bounding box */
std::sort(polygons_.begin(), polygons_.end(), [](PolygonData* a, PolygonData* b) {
return a->boundingMin.lat_ < b->boundingMin.lat_;
});
/* Encode data section and store pointers */
std::vector<uint8_t> outputData;
for(PolygonData* polygon: polygons_) {
polygon->fileIndex_ = outputData.size();
polygon->encodeBinaryData(outputData, precision);
}
std::cout << "Encoded data section into "<<outputData.size()<<" bytes.\n";
/* Encode metadata */
std::vector<uint8_t> outputMeta;
for(MetaData& metadata: metadata_) {
metadata.fileIndex_ = outputMeta.size();
metadata.encodeBinaryData(outputMeta);
}
std::cout << "Encoded metadata into "<<outputMeta.size()<<" bytes.\n";
/* Encode bounding boxes */
std::vector<uint8_t> outputBBox;
int64_t prevFileIndex = 0;
int64_t prevMetaIndex = 0;
for(PolygonData* polygon: polygons_) {
polygon->boundingMin.encodePointBinary(outputBBox, precision);
polygon->boundingMax.encodePointBinary(outputBBox, precision);
encodeVariableLength(outputBBox, metadata_.at(polygon->metadataId_).fileIndex_ - prevMetaIndex);
prevMetaIndex = metadata_[polygon->metadataId_].fileIndex_;
encodeVariableLength(outputBBox, polygon->fileIndex_ - prevFileIndex, false);
prevFileIndex = polygon->fileIndex_;
}
std::cout << "Encoded bounding box section into "<<outputBBox.size()<<" bytes.\n";
/* Encode header */
std::vector<uint8_t> outputHeader;
outputHeader.push_back('P');
outputHeader.push_back('L');
outputHeader.push_back('B');
outputHeader.push_back(tableType);
outputHeader.push_back(0);
outputHeader.push_back(precision);
outputHeader.push_back(fieldNames_.size());
for(unsigned int i=0; i<fieldNames_.size(); i++) {
encodeStringToBinary(outputHeader, fieldNames_[i]);
}
encodeStringToBinary(outputHeader, notice);
encodeVariableLength(outputHeader, outputBBox.size(), false);
encodeVariableLength(outputHeader, outputMeta.size(), false);
encodeVariableLength(outputHeader, outputData.size(), false);
std::cout << "Encoded header into "<<outputHeader.size()<<" bytes.\n";
FILE* outputFile = fopen(outPath.c_str(), "wb");
fwrite(outputHeader.data(), 1, outputHeader.size(), outputFile);
fwrite(outputBBox.data(), 1, outputBBox.size(), outputFile);
fwrite(outputMeta.data(), 1, outputMeta.size(), outputFile);
fwrite(outputData.data(), 1, outputData.size(), outputFile);
fclose(outputFile);
}

21
database/builder/makedb.sh Executable file
View file

@ -0,0 +1,21 @@
#!/bin/sh
g++ builder.cpp -o builder -lshp
rm -rf out naturalearth timezone db.zip
mkdir out
mkdir naturalearth; cd naturalearth
wget https://www.naturalearthdata.com/http//www.naturalearthdata.com/download/10m/cultural/ne_10m_admin_0_countries_lakes.zip
unzip ne_10m_admin_0_countries_lakes.zip
cd ..
./builder C naturalearth/ne_10m_admin_0_countries_lakes ./out/country16.bin 16 "Made with Natural Earth, placed in the Public Domain."
./builder C naturalearth/ne_10m_admin_0_countries_lakes ./out/country21.bin 21 "Made with Natural Earth, placed in the Public Domain."
mkdir timezone; cd timezone
wget https://github.com/evansiroky/timezone-boundary-builder/releases/download/2018i/timezones.shapefile.zip
unzip timezones.shapefile.zip
cd ..
./builder T timezone/dist/combined-shapefile ./out/timezone16.bin 16 "Contains data from Natural Earth, placed in the Public Domain. Contains information from https://github.com/evansiroky/timezone-boundary-builder, which is made available here under the Open Database License (ODbL)."
./builder T timezone/dist/combined-shapefile ./out/timezone21.bin 21 "Contains data from Natural Earth, placed in the Public Domain. Contains information from https://github.com/evansiroky/timezone-boundary-builder, which is made available here under the Open Database License (ODbL)."
rm -rf naturalearth
zip db.zip out/*

View file

@ -0,0 +1,425 @@
result["Europe/Andorra"] = "AD";
result["Asia/Dubai"] = "AE";
result["Asia/Kabul"] = "AF";
result["America/Antigua"] = "AG";
result["America/Anguilla"] = "AI";
result["Europe/Tirane"] = "AL";
result["Asia/Yerevan"] = "AM";
result["Africa/Luanda"] = "AO";
result["Antarctica/McMurdo"] = "AQ";
result["Antarctica/Casey"] = "AQ";
result["Antarctica/Davis"] = "AQ";
result["Antarctica/DumontDUrville"] = "AQ";
result["Antarctica/Mawson"] = "AQ";
result["Antarctica/Palmer"] = "AQ";
result["Antarctica/Rothera"] = "AQ";
result["Antarctica/Syowa"] = "AQ";
result["Antarctica/Troll"] = "AQ";
result["Antarctica/Vostok"] = "AQ";
result["America/Argentina/Buenos_Aires"] = "AR";
result["America/Argentina/Cordoba"] = "AR";
result["America/Argentina/Salta"] = "AR";
result["America/Argentina/Jujuy"] = "AR";
result["America/Argentina/Tucuman"] = "AR";
result["America/Argentina/Catamarca"] = "AR";
result["America/Argentina/La_Rioja"] = "AR";
result["America/Argentina/San_Juan"] = "AR";
result["America/Argentina/Mendoza"] = "AR";
result["America/Argentina/San_Luis"] = "AR";
result["America/Argentina/Rio_Gallegos"] = "AR";
result["America/Argentina/Ushuaia"] = "AR";
result["Pacific/Pago_Pago"] = "AS";
result["Europe/Vienna"] = "AT";
result["Australia/Lord_Howe"] = "AU";
result["Antarctica/Macquarie"] = "AU";
result["Australia/Hobart"] = "AU";
result["Australia/Currie"] = "AU";
result["Australia/Melbourne"] = "AU";
result["Australia/Sydney"] = "AU";
result["Australia/Broken_Hill"] = "AU";
result["Australia/Brisbane"] = "AU";
result["Australia/Lindeman"] = "AU";
result["Australia/Adelaide"] = "AU";
result["Australia/Darwin"] = "AU";
result["Australia/Perth"] = "AU";
result["Australia/Eucla"] = "AU";
result["America/Aruba"] = "AW";
result["Europe/Mariehamn"] = "AX";
result["Asia/Baku"] = "AZ";
result["Europe/Sarajevo"] = "BA";
result["America/Barbados"] = "BB";
result["Asia/Dhaka"] = "BD";
result["Europe/Brussels"] = "BE";
result["Africa/Ouagadougou"] = "BF";
result["Europe/Sofia"] = "BG";
result["Asia/Bahrain"] = "BH";
result["Africa/Bujumbura"] = "BI";
result["Africa/Porto-Novo"] = "BJ";
result["America/St_Barthelemy"] = "BL";
result["Atlantic/Bermuda"] = "BM";
result["Asia/Brunei"] = "BN";
result["America/La_Paz"] = "BO";
result["America/Kralendijk"] = "BQ";
result["America/Noronha"] = "BR";
result["America/Belem"] = "BR";
result["America/Fortaleza"] = "BR";
result["America/Recife"] = "BR";
result["America/Araguaina"] = "BR";
result["America/Maceio"] = "BR";
result["America/Bahia"] = "BR";
result["America/Sao_Paulo"] = "BR";
result["America/Campo_Grande"] = "BR";
result["America/Cuiaba"] = "BR";
result["America/Santarem"] = "BR";
result["America/Porto_Velho"] = "BR";
result["America/Boa_Vista"] = "BR";
result["America/Manaus"] = "BR";
result["America/Eirunepe"] = "BR";
result["America/Rio_Branco"] = "BR";
result["America/Nassau"] = "BS";
result["Asia/Thimphu"] = "BT";
result["Africa/Gaborone"] = "BW";
result["Europe/Minsk"] = "BY";
result["America/Belize"] = "BZ";
result["America/St_Johns"] = "CA";
result["America/Halifax"] = "CA";
result["America/Glace_Bay"] = "CA";
result["America/Moncton"] = "CA";
result["America/Goose_Bay"] = "CA";
result["America/Blanc-Sablon"] = "CA";
result["America/Toronto"] = "CA";
result["America/Nipigon"] = "CA";
result["America/Thunder_Bay"] = "CA";
result["America/Iqaluit"] = "CA";
result["America/Pangnirtung"] = "CA";
result["America/Atikokan"] = "CA";
result["America/Winnipeg"] = "CA";
result["America/Rainy_River"] = "CA";
result["America/Resolute"] = "CA";
result["America/Rankin_Inlet"] = "CA";
result["America/Regina"] = "CA";
result["America/Swift_Current"] = "CA";
result["America/Edmonton"] = "CA";
result["America/Cambridge_Bay"] = "CA";
result["America/Yellowknife"] = "CA";
result["America/Inuvik"] = "CA";
result["America/Creston"] = "CA";
result["America/Dawson_Creek"] = "CA";
result["America/Fort_Nelson"] = "CA";
result["America/Vancouver"] = "CA";
result["America/Whitehorse"] = "CA";
result["America/Dawson"] = "CA";
result["Indian/Cocos"] = "CC";
result["Africa/Kinshasa"] = "CD";
result["Africa/Lubumbashi"] = "CD";
result["Africa/Bangui"] = "CF";
result["Africa/Brazzaville"] = "CG";
result["Europe/Zurich"] = "CH";
result["Africa/Abidjan"] = "CI";
result["Pacific/Rarotonga"] = "CK";
result["America/Santiago"] = "CL";
result["America/Punta_Arenas"] = "CL";
result["Pacific/Easter"] = "CL";
result["Africa/Douala"] = "CM";
result["Asia/Shanghai"] = "CN";
result["Asia/Urumqi"] = "CN";
result["America/Bogota"] = "CO";
result["America/Costa_Rica"] = "CR";
result["America/Havana"] = "CU";
result["Atlantic/Cape_Verde"] = "CV";
result["America/Curacao"] = "CW";
result["Indian/Christmas"] = "CX";
result["Asia/Nicosia"] = "CY";
result["Asia/Famagusta"] = "CY";
result["Europe/Prague"] = "CZ";
result["Europe/Berlin"] = "DE";
result["Europe/Busingen"] = "DE";
result["Africa/Djibouti"] = "DJ";
result["Europe/Copenhagen"] = "DK";
result["America/Dominica"] = "DM";
result["America/Santo_Domingo"] = "DO";
result["Africa/Algiers"] = "DZ";
result["America/Guayaquil"] = "EC";
result["Pacific/Galapagos"] = "EC";
result["Europe/Tallinn"] = "EE";
result["Africa/Cairo"] = "EG";
result["Africa/El_Aaiun"] = "EH";
result["Africa/Asmara"] = "ER";
result["Europe/Madrid"] = "ES";
result["Africa/Ceuta"] = "ES";
result["Atlantic/Canary"] = "ES";
result["Africa/Addis_Ababa"] = "ET";
result["Europe/Helsinki"] = "FI";
result["Pacific/Fiji"] = "FJ";
result["Atlantic/Stanley"] = "FK";
result["Pacific/Chuuk"] = "FM";
result["Pacific/Pohnpei"] = "FM";
result["Pacific/Kosrae"] = "FM";
result["Atlantic/Faroe"] = "FO";
result["Europe/Paris"] = "FR";
result["Africa/Libreville"] = "GA";
result["Europe/London"] = "GB";
result["America/Grenada"] = "GD";
result["Asia/Tbilisi"] = "GE";
result["America/Cayenne"] = "GF";
result["Europe/Guernsey"] = "GG";
result["Africa/Accra"] = "GH";
result["Europe/Gibraltar"] = "GI";
result["America/Godthab"] = "GL";
result["America/Danmarkshavn"] = "GL";
result["America/Scoresbysund"] = "GL";
result["America/Thule"] = "GL";
result["Africa/Banjul"] = "GM";
result["Africa/Conakry"] = "GN";
result["America/Guadeloupe"] = "GP";
result["Africa/Malabo"] = "GQ";
result["Europe/Athens"] = "GR";
result["Atlantic/South_Georgia"] = "GS";
result["America/Guatemala"] = "GT";
result["Pacific/Guam"] = "GU";
result["Africa/Bissau"] = "GW";
result["America/Guyana"] = "GY";
result["Asia/Hong_Kong"] = "HK";
result["America/Tegucigalpa"] = "HN";
result["Europe/Zagreb"] = "HR";
result["America/Port-au-Prince"] = "HT";
result["Europe/Budapest"] = "HU";
result["Asia/Jakarta"] = "ID";
result["Asia/Pontianak"] = "ID";
result["Asia/Makassar"] = "ID";
result["Asia/Jayapura"] = "ID";
result["Europe/Dublin"] = "IE";
result["Asia/Jerusalem"] = "IL";
result["Europe/Isle_of_Man"] = "IM";
result["Asia/Kolkata"] = "IN";
result["Indian/Chagos"] = "IO";
result["Asia/Baghdad"] = "IQ";
result["Asia/Tehran"] = "IR";
result["Atlantic/Reykjavik"] = "IS";
result["Europe/Rome"] = "IT";
result["Europe/Jersey"] = "JE";
result["America/Jamaica"] = "JM";
result["Asia/Amman"] = "JO";
result["Asia/Tokyo"] = "JP";
result["Africa/Nairobi"] = "KE";
result["Asia/Bishkek"] = "KG";
result["Asia/Phnom_Penh"] = "KH";
result["Pacific/Tarawa"] = "KI";
result["Pacific/Enderbury"] = "KI";
result["Pacific/Kiritimati"] = "KI";
result["Indian/Comoro"] = "KM";
result["America/St_Kitts"] = "KN";
result["Asia/Pyongyang"] = "KP";
result["Asia/Seoul"] = "KR";
result["Asia/Kuwait"] = "KW";
result["America/Cayman"] = "KY";
result["Asia/Almaty"] = "KZ";
result["Asia/Qyzylorda"] = "KZ";
result["Asia/Aqtobe"] = "KZ";
result["Asia/Aqtau"] = "KZ";
result["Asia/Atyrau"] = "KZ";
result["Asia/Oral"] = "KZ";
result["Asia/Vientiane"] = "LA";
result["Asia/Beirut"] = "LB";
result["America/St_Lucia"] = "LC";
result["Europe/Vaduz"] = "LI";
result["Asia/Colombo"] = "LK";
result["Africa/Monrovia"] = "LR";
result["Africa/Maseru"] = "LS";
result["Europe/Vilnius"] = "LT";
result["Europe/Luxembourg"] = "LU";
result["Europe/Riga"] = "LV";
result["Africa/Tripoli"] = "LY";
result["Africa/Casablanca"] = "MA";
result["Europe/Monaco"] = "MC";
result["Europe/Chisinau"] = "MD";
result["Europe/Podgorica"] = "ME";
result["America/Marigot"] = "MF";
result["Indian/Antananarivo"] = "MG";
result["Pacific/Majuro"] = "MH";
result["Pacific/Kwajalein"] = "MH";
result["Europe/Skopje"] = "MK";
result["Africa/Bamako"] = "ML";
result["Asia/Yangon"] = "MM";
result["Asia/Ulaanbaatar"] = "MN";
result["Asia/Hovd"] = "MN";
result["Asia/Choibalsan"] = "MN";
result["Asia/Macau"] = "MO";
result["Pacific/Saipan"] = "MP";
result["America/Martinique"] = "MQ";
result["Africa/Nouakchott"] = "MR";
result["America/Montserrat"] = "MS";
result["Europe/Malta"] = "MT";
result["Indian/Mauritius"] = "MU";
result["Indian/Maldives"] = "MV";
result["Africa/Blantyre"] = "MW";
result["America/Mexico_City"] = "MX";
result["America/Cancun"] = "MX";
result["America/Merida"] = "MX";
result["America/Monterrey"] = "MX";
result["America/Matamoros"] = "MX";
result["America/Mazatlan"] = "MX";
result["America/Chihuahua"] = "MX";
result["America/Ojinaga"] = "MX";
result["America/Hermosillo"] = "MX";
result["America/Tijuana"] = "MX";
result["America/Bahia_Banderas"] = "MX";
result["Asia/Kuala_Lumpur"] = "MY";
result["Asia/Qostanay"] = "KZ";
result["Asia/Kuching"] = "MY";
result["Africa/Maputo"] = "MZ";
result["Africa/Windhoek"] = "NA";
result["Pacific/Noumea"] = "NC";
result["Africa/Niamey"] = "NE";
result["Pacific/Norfolk"] = "NF";
result["Africa/Lagos"] = "NG";
result["America/Managua"] = "NI";
result["Europe/Amsterdam"] = "NL";
result["Europe/Oslo"] = "NO";
result["Asia/Kathmandu"] = "NP";
result["Pacific/Nauru"] = "NR";
result["Pacific/Niue"] = "NU";
result["Pacific/Auckland"] = "NZ";
result["Pacific/Chatham"] = "NZ";
result["Asia/Muscat"] = "OM";
result["America/Panama"] = "PA";
result["America/Lima"] = "PE";
result["Pacific/Tahiti"] = "PF";
result["Pacific/Marquesas"] = "PF";
result["Pacific/Gambier"] = "PF";
result["Pacific/Port_Moresby"] = "PG";
result["Pacific/Bougainville"] = "PG";
result["Asia/Manila"] = "PH";
result["Asia/Karachi"] = "PK";
result["Europe/Warsaw"] = "PL";
result["America/Miquelon"] = "PM";
result["Pacific/Pitcairn"] = "PN";
result["America/Puerto_Rico"] = "PR";
result["Asia/Gaza"] = "PS";
result["Asia/Hebron"] = "PS";
result["Europe/Lisbon"] = "PT";
result["Atlantic/Madeira"] = "PT";
result["Atlantic/Azores"] = "PT";
result["Pacific/Palau"] = "PW";
result["America/Asuncion"] = "PY";
result["Asia/Qatar"] = "QA";
result["Indian/Reunion"] = "RE";
result["Europe/Bucharest"] = "RO";
result["Europe/Belgrade"] = "RS";
result["Europe/Kaliningrad"] = "RU";
result["Europe/Moscow"] = "RU";
result["Europe/Simferopol"] = "RU";
result["Europe/Volgograd"] = "RU";
result["Europe/Kirov"] = "RU";
result["Europe/Astrakhan"] = "RU";
result["Europe/Saratov"] = "RU";
result["Europe/Ulyanovsk"] = "RU";
result["Europe/Samara"] = "RU";
result["Asia/Yekaterinburg"] = "RU";
result["Asia/Omsk"] = "RU";
result["Asia/Novosibirsk"] = "RU";
result["Asia/Barnaul"] = "RU";
result["Asia/Tomsk"] = "RU";
result["Asia/Novokuznetsk"] = "RU";
result["Asia/Krasnoyarsk"] = "RU";
result["Asia/Irkutsk"] = "RU";
result["Asia/Chita"] = "RU";
result["Asia/Yakutsk"] = "RU";
result["Asia/Khandyga"] = "RU";
result["Asia/Vladivostok"] = "RU";
result["Asia/Ust-Nera"] = "RU";
result["Asia/Magadan"] = "RU";
result["Asia/Sakhalin"] = "RU";
result["Asia/Srednekolymsk"] = "RU";
result["Asia/Kamchatka"] = "RU";
result["Asia/Anadyr"] = "RU";
result["Africa/Kigali"] = "RW";
result["Asia/Riyadh"] = "SA";
result["Pacific/Guadalcanal"] = "SB";
result["Indian/Mahe"] = "SC";
result["Africa/Khartoum"] = "SD";
result["Europe/Stockholm"] = "SE";
result["Asia/Singapore"] = "SG";
result["Atlantic/St_Helena"] = "SH";
result["Europe/Ljubljana"] = "SI";
result["Arctic/Longyearbyen"] = "SJ";
result["Europe/Bratislava"] = "SK";
result["Africa/Freetown"] = "SL";
result["Europe/San_Marino"] = "SM";
result["Africa/Dakar"] = "SN";
result["Africa/Mogadishu"] = "SO";
result["America/Paramaribo"] = "SR";
result["Africa/Juba"] = "SS";
result["Africa/Sao_Tome"] = "ST";
result["America/El_Salvador"] = "SV";
result["America/Lower_Princes"] = "SX";
result["Asia/Damascus"] = "SY";
result["Africa/Mbabane"] = "SZ";
result["America/Grand_Turk"] = "TC";
result["Africa/Ndjamena"] = "TD";
result["Indian/Kerguelen"] = "TF";
result["Africa/Lome"] = "TG";
result["Asia/Bangkok"] = "TH";
result["Asia/Dushanbe"] = "TJ";
result["Pacific/Fakaofo"] = "TK";
result["Asia/Dili"] = "TL";
result["Asia/Ashgabat"] = "TM";
result["Africa/Tunis"] = "TN";
result["Pacific/Tongatapu"] = "TO";
result["Europe/Istanbul"] = "TR";
result["America/Port_of_Spain"] = "TT";
result["Pacific/Funafuti"] = "TV";
result["Asia/Taipei"] = "TW";
result["Africa/Dar_es_Salaam"] = "TZ";
result["Europe/Kiev"] = "UA";
result["Europe/Uzhgorod"] = "UA";
result["Europe/Zaporozhye"] = "UA";
result["Africa/Kampala"] = "UG";
result["Pacific/Midway"] = "UM";
result["Pacific/Wake"] = "UM";
result["America/New_York"] = "US";
result["America/Detroit"] = "US";
result["America/Kentucky/Louisville"] = "US";
result["America/Kentucky/Monticello"] = "US";
result["America/Indiana/Indianapolis"] = "US";
result["America/Indiana/Vincennes"] = "US";
result["America/Indiana/Winamac"] = "US";
result["America/Indiana/Marengo"] = "US";
result["America/Indiana/Petersburg"] = "US";
result["America/Indiana/Vevay"] = "US";
result["America/Chicago"] = "US";
result["America/Indiana/Tell_City"] = "US";
result["America/Indiana/Knox"] = "US";
result["America/Menominee"] = "US";
result["America/North_Dakota/Center"] = "US";
result["America/North_Dakota/New_Salem"] = "US";
result["America/North_Dakota/Beulah"] = "US";
result["America/Denver"] = "US";
result["America/Boise"] = "US";
result["America/Phoenix"] = "US";
result["America/Los_Angeles"] = "US";
result["America/Anchorage"] = "US";
result["America/Juneau"] = "US";
result["America/Sitka"] = "US";
result["America/Metlakatla"] = "US";
result["America/Yakutat"] = "US";
result["America/Nome"] = "US";
result["America/Adak"] = "US";
result["Pacific/Honolulu"] = "US";
result["America/Montevideo"] = "UY";
result["Asia/Samarkand"] = "UZ";
result["Asia/Tashkent"] = "UZ";
result["Europe/Vatican"] = "VA";
result["America/St_Vincent"] = "VC";
result["America/Caracas"] = "VE";
result["America/Tortola"] = "VG";
result["America/St_Thomas"] = "VI";
result["Asia/Ho_Chi_Minh"] = "VN";
result["Pacific/Efate"] = "VU";
result["Pacific/Wallis"] = "WF";
result["Pacific/Apia"] = "WS";
result["Asia/Aden"] = "YE";
result["Indian/Mayotte"] = "YT";
result["Africa/Johannesburg"] = "ZA";
result["Africa/Lusaka"] = "ZM";
result["Africa/Harare"] = "ZW";

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.