Initial commit
This commit is contained in:
commit
e2b3e1ebc1
16
.gitignore
vendored
Normal file
16
.gitignore
vendored
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
.cache/
|
||||||
|
.direnv/
|
||||||
|
build*/
|
||||||
|
result*
|
||||||
|
|
||||||
|
/out/
|
||||||
|
/fires/
|
||||||
|
|
||||||
|
*.csv
|
||||||
|
|
||||||
|
*.sb[nx]
|
||||||
|
*.sh[px]
|
||||||
|
*.shp.xml
|
||||||
|
*.cpg
|
||||||
|
*.dbf
|
||||||
|
*.prj
|
20
default.nix
Normal file
20
default.nix
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
lib,
|
||||||
|
stdenv,
|
||||||
|
meson,
|
||||||
|
pkg-config,
|
||||||
|
ninja,
|
||||||
|
geographiclib,
|
||||||
|
shapelib,
|
||||||
|
strip ? false,
|
||||||
|
}:
|
||||||
|
stdenv.mkDerivation {
|
||||||
|
name = "shapething";
|
||||||
|
src = lib.cleanSource ./.;
|
||||||
|
|
||||||
|
nativeBuildInputs = [meson pkg-config ninja];
|
||||||
|
buildInputs = [geographiclib shapelib];
|
||||||
|
|
||||||
|
mesonBuildType = "release";
|
||||||
|
mesonFlags = lib.optional strip "--strip";
|
||||||
|
}
|
26
flake.lock
Normal file
26
flake.lock
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1704194953,
|
||||||
|
"narHash": "sha256-RtDKd8Mynhe5CFnVT8s0/0yqtWFMM9LmCzXv/YKxnq4=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "bd645e8668ec6612439a9ee7e71f7eac4099d4f6",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"id": "nixpkgs",
|
||||||
|
"ref": "nixos-unstable",
|
||||||
|
"type": "indirect"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": "nixpkgs"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
42
flake.nix
Normal file
42
flake.nix
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
{
|
||||||
|
inputs.nixpkgs.url = "nixpkgs/nixos-unstable";
|
||||||
|
|
||||||
|
outputs = {
|
||||||
|
self,
|
||||||
|
nixpkgs,
|
||||||
|
}: let
|
||||||
|
inherit (nixpkgs) lib;
|
||||||
|
genSystems = lib.genAttrs [
|
||||||
|
"x86_64-linux"
|
||||||
|
"aarch64-linux"
|
||||||
|
];
|
||||||
|
pkgsFor = system: import nixpkgs {inherit system;};
|
||||||
|
in {
|
||||||
|
packages = genSystems (
|
||||||
|
system: let
|
||||||
|
pkgs = pkgsFor system;
|
||||||
|
in {
|
||||||
|
default = pkgs.callPackage ./. {};
|
||||||
|
static = pkgs.pkgsStatic.callPackage ./. {
|
||||||
|
strip = true;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
devShells = genSystems (
|
||||||
|
system: let
|
||||||
|
pkgs = pkgsFor system;
|
||||||
|
in {
|
||||||
|
default = pkgs.mkShell {
|
||||||
|
nativeBuildInputs = with pkgs; [clang-tools];
|
||||||
|
inputsFrom = [self.packages.${system}.default];
|
||||||
|
};
|
||||||
|
pyShell = pkgs.mkShell {
|
||||||
|
nativeBuildInputs = [(pkgs.python3.withPackages (ps: with ps; [geopandas]))];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
formatter = genSystems (system: (pkgsFor system).alejandra);
|
||||||
|
};
|
||||||
|
}
|
17
meson.build
Normal file
17
meson.build
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
project('shapefile-test', 'cpp',
|
||||||
|
default_options : [
|
||||||
|
'cpp_std=c++20',
|
||||||
|
'warning_level=3',
|
||||||
|
'buildtype=release',
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
executable(
|
||||||
|
'shapething',
|
||||||
|
'src/main.cpp',
|
||||||
|
dependencies : [
|
||||||
|
dependency('geographiclib'),
|
||||||
|
dependency('shapelib'),
|
||||||
|
],
|
||||||
|
install : true
|
||||||
|
)
|
225
src/main.cpp
Normal file
225
src/main.cpp
Normal file
|
@ -0,0 +1,225 @@
|
||||||
|
#include <filesystem>
|
||||||
|
#include <fstream>
|
||||||
|
#include <GeographicLib/UTMUPS.hpp>
|
||||||
|
#include <iostream>
|
||||||
|
#include <map>
|
||||||
|
#include <mutex>
|
||||||
|
#include <shapefil.h>
|
||||||
|
#include <thread>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
struct month_info {
|
||||||
|
unsigned long long n_fires;
|
||||||
|
std::string name;
|
||||||
|
std::map<std::string, std::vector<std::pair<double, double>>> days;
|
||||||
|
};
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
unsigned int distance = 2'000;
|
||||||
|
std::string fire_dir = "fires";
|
||||||
|
std::filesystem::path out_dir = "out";
|
||||||
|
auto do_sort = false;
|
||||||
|
int c;
|
||||||
|
while ((c = getopt(argc, argv, "d:f:o:s")) != -1) {
|
||||||
|
switch (c) {
|
||||||
|
case 'd':
|
||||||
|
distance = atoi(optarg);
|
||||||
|
break;
|
||||||
|
case 'f':
|
||||||
|
fire_dir = optarg;
|
||||||
|
break;
|
||||||
|
case 'o':
|
||||||
|
out_dir = optarg;
|
||||||
|
break;
|
||||||
|
case 's':
|
||||||
|
do_sort = true;
|
||||||
|
break;
|
||||||
|
case '?':
|
||||||
|
switch (optopt) {
|
||||||
|
case 'd':
|
||||||
|
std::cerr << "Option -d requires a distance argument" << std::endl;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
std::cerr << "Unknown option " << optopt << std::endl;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
char *res_loc;
|
||||||
|
switch (argc - optind) {
|
||||||
|
case 0: {
|
||||||
|
res_loc = NULL;
|
||||||
|
for (const auto &FE : std::filesystem::directory_iterator(".")) {
|
||||||
|
if (!FE.is_regular_file())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const auto FP = FE.path();
|
||||||
|
if (FP.extension() != ".shp")
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (res_loc != NULL) {
|
||||||
|
free(res_loc);
|
||||||
|
res_loc = NULL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string FPS = FP;
|
||||||
|
res_loc = static_cast<char *>(malloc((FPS.size() + 1) * sizeof(*res_loc)));
|
||||||
|
FPS.copy(res_loc, FPS.size());
|
||||||
|
res_loc[FPS.size()] = '\0';
|
||||||
|
}
|
||||||
|
if (res_loc == NULL) {
|
||||||
|
std::cerr << "Failed to automatically find residents shapefile!" << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 1:
|
||||||
|
res_loc = argv[optind];
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
std::cerr << "Too many arguments!" << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto RES_H = SHPOpen(res_loc, "rb");
|
||||||
|
|
||||||
|
std::cout << "Reading residents data..." << std::endl;
|
||||||
|
int res_n, res_type;
|
||||||
|
SHPGetInfo(RES_H, &res_n, &res_type, NULL, NULL);
|
||||||
|
if (res_type != SHPT_POINT) {
|
||||||
|
std::cerr << "Expected point data from " << res_loc << "!" << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::pair<double, double>> residentials;
|
||||||
|
for (auto i = 0; i < res_n; i++) {
|
||||||
|
const auto RES_P = SHPReadObject(RES_H, i);
|
||||||
|
residentials.push_back(std::make_pair(RES_P->padfX[0], RES_P->padfY[0]));
|
||||||
|
SHPDestroyObject(RES_P);
|
||||||
|
}
|
||||||
|
|
||||||
|
SHPClose(RES_H);
|
||||||
|
|
||||||
|
|
||||||
|
std::cout << "Reading fires data..." << std::endl;
|
||||||
|
std::vector<month_info> all_fires;
|
||||||
|
for (const auto& MONTH_ENTRY : std::filesystem::directory_iterator(fire_dir)) {
|
||||||
|
if (!std::filesystem::is_directory(MONTH_ENTRY))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const std::string MONTH = MONTH_ENTRY.path().filename();
|
||||||
|
std::map<std::string, std::vector<std::pair<double, double>>> all_days;
|
||||||
|
unsigned long long n_fires_month = 0;
|
||||||
|
|
||||||
|
for (const auto& DAY_ENTRY : std::filesystem::directory_iterator(MONTH_ENTRY)) {
|
||||||
|
const auto FP = DAY_ENTRY.path();
|
||||||
|
if (FP.extension() != ".shp")
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const auto DAY = FP.stem().string().substr(8);
|
||||||
|
|
||||||
|
const auto FIRE_H = SHPOpen(FP.c_str(), "rb");
|
||||||
|
int fire_n, fire_type;
|
||||||
|
SHPGetInfo(FIRE_H, &fire_n, &fire_type, NULL, NULL);
|
||||||
|
if (res_type != SHPT_POINT) {
|
||||||
|
std::cerr << "Expected point data from " << FP << "!" << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::pair<double, double>> fires;
|
||||||
|
for (auto i = 0; i < fire_n; i++) {
|
||||||
|
const auto FIRE_P = SHPReadObject(FIRE_H, i);
|
||||||
|
int zone;
|
||||||
|
bool northp;
|
||||||
|
double x, y;
|
||||||
|
GeographicLib::UTMUPS::Forward(FIRE_P->padfY[0], FIRE_P->padfX[0], zone, northp, x, y);
|
||||||
|
fires.push_back(std::make_pair(x, y));
|
||||||
|
SHPDestroyObject(FIRE_P);
|
||||||
|
}
|
||||||
|
|
||||||
|
SHPClose(FIRE_H);
|
||||||
|
|
||||||
|
if (do_sort)
|
||||||
|
n_fires_month += fire_n;
|
||||||
|
|
||||||
|
all_days[DAY] = std::move(fires);
|
||||||
|
}
|
||||||
|
|
||||||
|
all_fires.push_back(month_info{
|
||||||
|
.n_fires = n_fires_month,
|
||||||
|
.name = MONTH,
|
||||||
|
.days = std::move(all_days),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (do_sort) {
|
||||||
|
std::sort(all_fires.begin(), all_fires.end(),
|
||||||
|
[](auto& a, auto& b) { return a.n_fires > b.n_fires; });
|
||||||
|
}
|
||||||
|
|
||||||
|
std::filesystem::create_directory(out_dir);
|
||||||
|
|
||||||
|
std::vector<std::thread> workers;
|
||||||
|
std::mutex it_lock;
|
||||||
|
auto it = all_fires.begin();
|
||||||
|
auto wc = std::thread::hardware_concurrency();
|
||||||
|
if (wc == 0) {
|
||||||
|
wc = 1;
|
||||||
|
} else if (wc > 1) {
|
||||||
|
wc--;
|
||||||
|
}
|
||||||
|
std::cout << "Starting " << wc << " worker threads..." << std::endl;
|
||||||
|
for (unsigned int w = 1; w <= wc; w++) {
|
||||||
|
workers.push_back(std::thread([&it, &it_lock, &all_fires, &residentials,
|
||||||
|
&out_dir, w, distance]() {
|
||||||
|
for (;;) {
|
||||||
|
it_lock.lock();
|
||||||
|
if (it == all_fires.end()) {
|
||||||
|
std::cout << "Worker " << w << " done!" << std::endl;
|
||||||
|
it_lock.unlock();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& month_data = *(it++);
|
||||||
|
auto& month = month_data.name;
|
||||||
|
auto& month_fires = month_data.days;
|
||||||
|
std::cout << "Worker " << w << " processing data from " << month << "..." << std::endl;
|
||||||
|
it_lock.unlock();
|
||||||
|
|
||||||
|
std::ofstream outf(out_dir / (month + ".csv"));
|
||||||
|
outf << "id";
|
||||||
|
for (auto& month_data : month_fires) {
|
||||||
|
outf << ',' << month_data.first;
|
||||||
|
}
|
||||||
|
outf << '\n';
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < residentials.size(); i++) {
|
||||||
|
outf << i;
|
||||||
|
const auto& [resx, resy] = residentials[i];
|
||||||
|
for (const auto& [day, fires] : month_fires) {
|
||||||
|
auto matchp = 0;
|
||||||
|
for (const auto& [firex, firey] : fires) {
|
||||||
|
const auto dx = resx - firex;
|
||||||
|
const auto dy = resy - firey;
|
||||||
|
if (dx * dx + dy * dy < distance * distance) {
|
||||||
|
matchp = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
outf << ',' << matchp;
|
||||||
|
}
|
||||||
|
outf << '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& worker : workers) {
|
||||||
|
worker.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "All done!" << std::endl;
|
||||||
|
}
|
Loading…
Reference in a new issue