geostat/region.cpp

112 wiersze
3.5 KiB
C++

#include "region.h"
#include "debug.h"
#include <fstream>
#include <stdexcept>
#include <algorithm>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
typedef bg::model::point<double, 2, bg::cs::cartesian> point_t;
BoundingBox::BoundingBox(Position p1, Position p2) {
a.lat = std::min(p1.lat, p2.lat);
b.lat = std::max(p1.lat, p2.lat);
a.lon = std::min(p1.lon, p2.lon);
b.lon = std::max(p1.lon, p2.lon);
}
bool BoundingBox::within(Position p) const {
return (p.lat >= a.lat && p.lat <= b.lat && p.lon >= a.lon && p.lon <= b.lon);
}
bool ContourData::within(Position p) const {
return bbox.within(p) && bg::within(point_t{ p.lon, p.lat }, contour);
}
ContourData::ContourData(BoundingBox bbox) : bbox(bbox) {}
void ContourData::add_point(Position p) {
bg::append(contour, point_t{ p.lon, p.lat });
}
// TODO: Parse inner ring and multi-poligon
std::map<std::string, ContourData*> Region::parse_geojson(const std::string& json_file) {
if (!std::filesystem::exists(json_file)) {
throw std::runtime_error("File " + json_file + " does not exist.");
}
std::ifstream file(json_file);
json j;
file >> j;
std::string reg_name;
std::map<std::string, ContourData*> rr;
for (auto& el : j["features"].items()) {
if (el.value()["properties"]["alltags"].count("short_name") > 0)
reg_name = el.value()["properties"]["alltags"]["short_name"];
else if (el.value()["properties"].count("localname") > 0)
reg_name = el.value()["properties"]["localname"];
else if (el.value()["properties"].count("name") > 0)
reg_name = el.value()["properties"]["name"];
else {
Debug(1) << "Unknown name for an object, skipping";
continue;
}
ContourData* cont = new ContourData(BoundingBox(Position(el.value()["bbox"][1], el.value()["bbox"][0]), Position(el.value()["bbox"][3], el.value()["bbox"][2])));
for (auto& el2 : el.value()["geometry"]["coordinates"][0][0].items()) {
cont->add_point(Position(el2.value()[1], el2.value()[0]));
}
Debug(3) << "Parsed region " << reg_name;
rr[reg_name] = cont;
}
return rr;
}
Region::Region(const std::filesystem::path& geojson, const std::filesystem::path& geojson_sub) {
auto reg = *parse_geojson(geojson).begin();
name = reg.first;
contour = reg.second;
auto geodata = parse_geojson(geojson_sub);
std::transform(geodata.begin(), geodata.end(), std::back_inserter(subregions), [&](auto a) { return new Region(a.second, a.first); });
// for (auto& el : parse_geojson(geojson_sub))
// subregions.emplace_back(new Region(el.second, el.first));
}
Region::~Region() {
subregions.clear();
delete contour;
}
std::string Region::find_subregion(Position p) {
if (!within(p)) throw std::runtime_error("Point not in this region.");
auto res = std::find_if(subregions.begin(), subregions.end(), [&](auto a) { return a->within(p); });
if (res != subregions.end())
return (*res)->get_name();
return "";
// else
// throw std::runtime_error("Point " + std::to_string(p.lat) + " " + std::to_string(p.lon) + " not in any of the subregions.");
}
Country::Country(const std::vector<std::filesystem::path>& jsons) {
for (auto el : jsons) {
std::filesystem::path p1 = "geojson" / el;
std::filesystem::path p2 = p1;
p1 += ".geojson";
p2 += "_sub.geojson";
regions.emplace_back(new Region(p1, p2));
}
}
void Country::locate(Cache& c) {
auto el = std::find_if(regions.begin(), regions.end(), [&](auto a) { return a->within(c.pos); });
if (el != regions.end()) {
c.region = (*el)->get_name();
c.subregion = (*el)->find_subregion(c.pos);
Debug(3) << "Found subregion " << c.subregion;
}
}