From a56b7400a4d46097f459a6a90eae42ecb60b715f Mon Sep 17 00:00:00 2001 From: Dan Paulat Date: Sun, 3 Dec 2023 07:16:12 -0600 Subject: [PATCH] Add states and territories to county database --- data | 2 +- scwx-qt/scwx-qt.cmake | 7 ++- .../source/scwx/qt/config/county_database.cpp | 49 ++++++++++++++++--- scwx-qt/tools/generate_counties_db.py | 32 +++++++++++- 4 files changed, 79 insertions(+), 11 deletions(-) diff --git a/data b/data index 9b6c72f8..db52049e 160000 --- a/data +++ b/data @@ -1 +1 @@ -Subproject commit 9b6c72f847193bc29d3ff183b206f26a9b5c007e +Subproject commit db52049ea651fea92b06e5024cbff3a3d3d26bc8 diff --git a/scwx-qt/scwx-qt.cmake b/scwx-qt/scwx-qt.cmake index 44e0c0ae..b032971e 100644 --- a/scwx-qt/scwx-qt.cmake +++ b/scwx-qt/scwx-qt.cmake @@ -323,6 +323,7 @@ set(ZONE_DBF_FILES ${SCWX_DIR}/data/db/fz19se23.dbf ${SCWX_DIR}/data/db/mz19se23.dbf ${SCWX_DIR}/data/db/oz08mr23.dbf ${SCWX_DIR}/data/db/z_19se23.dbf) +set(STATE_DBF_FILES ${SCWX_DIR}/data/db/s_08mr23.dbf) set(COUNTIES_SQLITE_DB ${scwx-qt_BINARY_DIR}/res/db/counties.db) set(VERSIONS_INPUT ${scwx-qt_SOURCE_DIR}/source/scwx/qt/main/versions.hpp.in) @@ -411,8 +412,12 @@ add_custom_command(OUTPUT ${COUNTIES_SQLITE_DB} ${scwx-qt_SOURCE_DIR}/tools/generate_counties_db.py -c ${COUNTY_DBF_FILES} -z ${ZONE_DBF_FILES} + -s ${STATE_DBF_FILES} -o ${COUNTIES_SQLITE_DB} - DEPENDS ${COUNTY_DB_FILES} ${ZONE_DBF_FILES}) + DEPENDS ${scwx-qt_SOURCE_DIR}/tools/generate_counties_db.py + ${COUNTY_DB_FILES} + ${STATE_DBF_FILES} + ${ZONE_DBF_FILES}) add_custom_target(scwx-qt_generate_counties_db ALL DEPENDS ${COUNTIES_SQLITE_DB}) diff --git a/scwx-qt/source/scwx/qt/config/county_database.cpp b/scwx-qt/source/scwx/qt/config/county_database.cpp index a0d7b51c..a35db04f 100644 --- a/scwx-qt/source/scwx/qt/config/county_database.cpp +++ b/scwx-qt/source/scwx/qt/config/county_database.cpp @@ -28,6 +28,7 @@ static const std::string countyDatabaseFilename_ = ":/res/db/counties.db"; static bool initialized_ {false}; static std::unordered_map countyMap_; static std::shared_mutex countyMutex_; +static std::unordered_map stateMap_; void Initialize() { @@ -108,7 +109,41 @@ void Initialize() else { logger_->error( - "Database format error, invalid number of columns: {}", columns); + "County database format error, invalid number of columns: {}", + columns); + status = -1; + } + + return status; + }, + nullptr, + &errorMessage); + if (rc != SQLITE_OK) + { + logger_->error("SQL error: {}", errorMessage); + sqlite3_free(errorMessage); + } + + // Query database for states + rc = sqlite3_exec( + db, + "SELECT * FROM states", + [](void* /* param */, + int columns, + char** columnText, + char** /* columnName */) -> int + { + int status = 0; + + if (columns == 2) + { + stateMap_.emplace(columnText[0], columnText[1]); + } + else + { + logger_->error( + "State database format error, invalid number of columns: {}", + columns); status = -1; } @@ -129,13 +164,11 @@ void Initialize() sqlite3_close(db); // Remove temporary file - std::error_code err; - - if (!std::filesystem::remove(countyDatabaseCache, err)) { - logger_->warn( - "Unable to remove cached copy of database, error code: {} error category: {}", - err.value(), - err.category().name()); + std::error_code error; + if (!std::filesystem::remove(countyDatabaseCache, error)) + { + logger_->warn("Unable to remove cached copy of database: {}", + error.message()); } initialized_ = true; diff --git a/scwx-qt/tools/generate_counties_db.py b/scwx-qt/tools/generate_counties_db.py index 1cbc01bf..6605ef76 100644 --- a/scwx-qt/tools/generate_counties_db.py +++ b/scwx-qt/tools/generate_counties_db.py @@ -26,6 +26,14 @@ def ParseArguments(): nargs = "+", default = [], type = pathlib.Path) + parser.add_argument("-s", "--state_dbf", + metavar = "filename", + help = "input state database", + dest = "inputStateDbs_", + action = "extend", + nargs = "+", + default = [], + type = pathlib.Path) parser.add_argument("-o", "--output_db", metavar = "filename", help = "output sqlite database", @@ -47,10 +55,13 @@ def Prepare(dbInfo, outputDb): dbInfo.sqlCursor_ = dbInfo.sqlConnection_.cursor() - # Create database table + # Create database tables dbInfo.sqlCursor_.execute("""CREATE TABLE counties( id TEXT NOT NULL PRIMARY KEY, name TEXT)""") + dbInfo.sqlCursor_.execute("""CREATE TABLE states( + state TEXT NOT NULL PRIMARY KEY, + name TEXT NOT NULL)""") def ProcessCountiesDbf(dbInfo, dbfFilename): # County area type @@ -72,6 +83,22 @@ def ProcessCountiesDbf(dbInfo, dbfFilename): except: print("Skipping duplicate county:", fipsId, row.COUNTYNAME) +def ProcessStateDbf(dbInfo, dbfFilename): + print("Processing states and territories file:", dbfFilename) + + # Read dataframe + dbfTable = gpd.read_file(filename = dbfFilename, + include_fields = ["STATE", "NAME"], + ignore_geometry = True) + dbfTable.drop_duplicates(inplace=True) + + for row in dbfTable.itertuples(): + # Insert data into database + try: + dbInfo.sqlCursor_.execute("INSERT INTO states VALUES (?, ?)", (row.STATE, row.NAME)) + except: + print("Error inserting row:", row.STATE, row.NAME) + def ProcessZoneDbf(dbInfo, dbfFilename): print("Processing zone file:", dbfFilename) # Zone area type @@ -118,4 +145,7 @@ for countyDb in args.inputCountyDbs_: for zoneDb in args.inputZoneDbs_: ProcessZoneDbf(dbInfo, zoneDb) +for stateDb in args.inputStateDbs_: + ProcessStateDbf(dbInfo, stateDb) + PostProcess(dbInfo)