/******************************************************** * ██████╗ ██████╗████████╗██╗ * ██╔════╝ ██╔════╝╚══██╔══╝██║ * ██║ ███╗██║ ██║ ██║ * ██║ ██║██║ ██║ ██║ * ╚██████╔╝╚██████╗ ██║ ███████╗ * ╚═════╝ ╚═════╝ ╚═╝ ╚══════╝ * Geophysical Computational Tools & Library (GCTL) * * Copyright (c) 2023 Yi Zhang (yizhang-geo@zju.edu.cn) * * GCTL is distributed under a dual licensing scheme. You can redistribute * it and/or modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation, either version 2 * of the License, or (at your option) any later version. You should have * received a copy of the GNU Lesser General Public License along with this * program. If not, see . * * If the terms and conditions of the LGPL v.2. would prevent you from using * the GCTL, please consider the option to obtain a commercial license for a * fee. These licenses are offered by the GCTL's original author. As a rule, * licenses are provided "as-is", unlimited in time for a one time fee. Please * send corresponding requests to: yizhang-geo@zju.edu.cn. Please do not forget * to include some description of your company and the realm of its activities. * Also add information on how to contact you by electronic and paper mail. ******************************************************/ #ifndef _BOXSORT_SPH_H #define _BOXSORT_SPH_H #include "../core.h" #include "heap_sort.h" namespace gctl { template struct cargo_sph { double lon, lat; A* item; }; template struct box_sort_pair_sph { int id; double near_deg; std::vector > *box_ptr; }; /** * @brief This class implements the template class boxes_sph, which preforms the spherical degree based sorting of objects. * * @tparam T template type */ template class boxes_sph { public: boxes_sph(); boxes_sph(const array &lons, const array &lats, const array &items, size_t lon_bnum, size_t lat_bnum); virtual ~boxes_sph(); void init(const array &lons, const array &lats, const array &items, size_t lon_bnum, size_t lat_bnum); void get_by_angle(double inlon, double inlat, double indeg, std::vector &cargo_list, bool on_boundary = false); void get_by_number(double inlon, double inlat, size_t target_num, std::vector &cargo_list); double spherical_angle(double lon1, double lat1, double lon2, double lat2); protected: int lon_bsize_, lat_bsize_, item_size_; double lon_min_, lon_max_, lat_min_, lat_max_, dlon_, dlat_; array > > boxes_; bool initialized_; // 以下为get_by_number函数所需变量 array > box_pairs_; heap_sort > boxes_sorter_; array LocalDeg_; bool box_pairs_initalized_; }; template boxes_sph::boxes_sph() { initialized_ = false; box_pairs_initalized_ = false; } template boxes_sph::boxes_sph(const array &lons, const array &lats, const array &items, size_t lon_bnum, size_t lat_bnum) : boxes_sph() { init(lons, lats, items, lon_bnum, lat_bnum); } template boxes_sph::~boxes_sph() { if (!boxes_.empty()) { for (int i = 0; i < boxes_.size(); ++i) { boxes_[i].clear(); std::vector>().swap(boxes_[i]); } } } template void boxes_sph::init(const array &lons, const array &lats, const array &items, size_t lon_bnum, size_t lat_bnum) { if (lons.empty() || lats.empty() || items.empty()) { throw domain_error("Empty inputs. From boxes_sph::init(...)"); } if (lons.size() != lats.size() || lons.size() != items.size()) { throw invalid_argument("Unequal array sizes. From boxes_sph::init(...)"); } if (lon_bnum == 0 || lat_bnum == 0) { throw invalid_argument("Invalid box dimensions. From boxes_sph::init(...)"); } lon_bsize_ = lon_bnum; lat_bsize_ = lat_bnum; item_size_ = items.size(); boxes_.resize(lon_bsize_*lat_bsize_); lon_min_ = GCTL_BDL_MAX; lon_max_ = GCTL_BDL_MIN; lat_min_ = GCTL_BDL_MAX; lat_max_ = GCTL_BDL_MIN; for (size_t i = 0; i < item_size_; i++) { lon_min_ = std::min(lon_min_, lons[i]); lon_max_ = std::max(lon_max_, lons[i]); lat_min_ = std::min(lat_min_, lats[i]); lat_max_ = std::max(lat_max_, lats[i]); } lon_min_ = std::max(lon_min_, -180.0); lon_max_ = std::min(lon_max_, 180.0); lat_min_ = std::max(lat_min_, -90.0); lat_max_ = std::min(lat_max_, 90.0); dlon_ = (lon_max_ - lon_min_)/lon_bsize_; dlat_ = (lat_max_ - lat_min_)/lat_bsize_; int M, N; double lon, lat; cargo_sph tmp_cargo; for (size_t i = 0; i < item_size_; i++) { lon = std::max(-180.0, std::min(lons[i], 180.0)); lat = std::max(-90.0, std::min(lats[i], 90.0)); N = floor((lon - lon_min_)/dlon_); M = floor((lat - lat_min_)/dlat_); if (N == lon_bsize_) N--; if (M == lat_bsize_) M--; if (M >= 0 && M < lat_bsize_ && N >= 0 && N < lon_bsize_) { tmp_cargo.lon = lon; tmp_cargo.lat = lat; tmp_cargo.item = items.get(i); boxes_[N + M*lon_bsize_].push_back(tmp_cargo); } } initialized_ = true; return; } template void boxes_sph::get_by_angle(double inlon, double inlat, double indeg, std::vector &cargo_list, bool on_boundary) { if (!initialized_) { throw runtime_error("The boxes_sph object is not initialized_. From boxes_sph::get_by_angle(...)"); } if (!cargo_list.empty()) cargo_list.clear(); // sort all boxes_ by the closest possible angle size_t idx; double cor_lon, cor_lat; if (!box_pairs_initalized_) { box_pairs_.resize(lon_bsize_*lat_bsize_); for (size_t i = 0; i < lat_bsize_; i++) { for (size_t j = 0; j < lon_bsize_; j++) { idx = j+i*lon_bsize_; box_pairs_[idx].id = idx; box_pairs_[idx].near_deg = 180.0; box_pairs_[idx].box_ptr = boxes_.get(idx); cor_lon = lon_min_ + j*dlon_; cor_lat = lat_min_ + i*dlat_; box_pairs_[idx].near_deg = std::min(box_pairs_[idx].near_deg, spherical_angle(inlon, inlat, cor_lon, cor_lat)); cor_lon = lon_min_ + (j+1)*dlon_; cor_lat = lat_min_ + i*dlat_; box_pairs_[idx].near_deg = std::min(box_pairs_[idx].near_deg, spherical_angle(inlon, inlat, cor_lon, cor_lat)); cor_lon = lon_min_ + (j+1)*dlon_; cor_lat = lat_min_ + (i+1)*dlat_; box_pairs_[idx].near_deg = std::min(box_pairs_[idx].near_deg, spherical_angle(inlon, inlat, cor_lon, cor_lat)); cor_lon = lon_min_ + j*dlon_; cor_lat = lat_min_ + (i+1)*dlat_; box_pairs_[idx].near_deg = std::min(box_pairs_[idx].near_deg, spherical_angle(inlon, inlat, cor_lon, cor_lat)); } } box_pairs_initalized_ = true; } else { int M, N; for (size_t i = 0; i < box_pairs_.size(); i++) { M = box_pairs_[i].id/lon_bsize_; N = box_pairs_[i].id%lon_bsize_; cor_lon = lon_min_ + N*dlon_; cor_lat = lat_min_ + M*dlat_; box_pairs_[i].near_deg = std::min(box_pairs_[i].near_deg, spherical_angle(inlon, inlat, cor_lon, cor_lat)); cor_lon = lon_min_ + (N+1)*dlon_; cor_lat = lat_min_ + M*dlat_; box_pairs_[i].near_deg = std::min(box_pairs_[i].near_deg, spherical_angle(inlon, inlat, cor_lon, cor_lat)); cor_lon = lon_min_ + (N+1)*dlon_; cor_lat = lat_min_ + (M+1)*dlat_; box_pairs_[i].near_deg = std::min(box_pairs_[i].near_deg, spherical_angle(inlon, inlat, cor_lon, cor_lat)); cor_lon = lon_min_ + N*dlon_; cor_lat = lat_min_ + (M+1)*dlat_; box_pairs_[i].near_deg = std::min(box_pairs_[i].near_deg, spherical_angle(inlon, inlat, cor_lon, cor_lat)); } } // Sort boxes_' pointers by the closest possible distance boxes_sorter_.execute(box_pairs_, [](array> &a, int l_id, int r_id)->bool{ if (a[l_id].near_deg < a[r_id].near_deg) return true; else return false; }); double deg; for (size_t i = 0; i < box_pairs_.size(); i++) { if (box_pairs_[i].near_deg < indeg || (on_boundary && box_pairs_[i].near_deg == indeg)) { for (size_t j = 0; j < box_pairs_[i].box_ptr->size(); j++) { deg = spherical_angle(inlon, inlat, box_pairs_[i].box_ptr->at(j).lon, box_pairs_[i].box_ptr->at(j).lat); if (deg < indeg || (on_boundary && deg == indeg)) { cargo_list.push_back(box_pairs_[i].box_ptr->at(j).item); } } } break; // break at the first failed inquire } return; } template void boxes_sph::get_by_number(double inlon, double inlat, size_t target_num, std::vector &cargo_list) { if (!initialized_) { throw runtime_error("The boxes_sph object is not initialized_. From boxes_sph::get_by_number(...)"); } if (target_num <= 0) { throw invalid_argument("Invalid target number. From boxes_sph::get_by_number(...)"); } if (!cargo_list.empty()) cargo_list.clear(); cargo_list.resize(target_num, nullptr); int count = 0; if (target_num >= item_size_) { for (int i = 0; i < lon_bsize_*lat_bsize_; ++i) { for (int j = 0; j < boxes_[i].size(); ++j) { cargo_list[count] = boxes_[i][j].item; count++; } } return; } // sort all boxes_ by the closest possible angle size_t idx; double cor_lon, cor_lat; if (!box_pairs_initalized_) { box_pairs_.resize(lon_bsize_*lat_bsize_); for (size_t i = 0; i < lat_bsize_; i++) { for (size_t j = 0; j < lon_bsize_; j++) { idx = j+i*lon_bsize_; box_pairs_[idx].id = idx; box_pairs_[idx].near_deg = 180.0; box_pairs_[idx].box_ptr = boxes_.get(idx); cor_lon = lon_min_ + j*dlon_; cor_lat = lat_min_ + i*dlat_; box_pairs_[idx].near_deg = std::min(box_pairs_[idx].near_deg, spherical_angle(inlon, inlat, cor_lon, cor_lat)); cor_lon = lon_min_ + (j+1)*dlon_; cor_lat = lat_min_ + i*dlat_; box_pairs_[idx].near_deg = std::min(box_pairs_[idx].near_deg, spherical_angle(inlon, inlat, cor_lon, cor_lat)); cor_lon = lon_min_ + (j+1)*dlon_; cor_lat = lat_min_ + (i+1)*dlat_; box_pairs_[idx].near_deg = std::min(box_pairs_[idx].near_deg, spherical_angle(inlon, inlat, cor_lon, cor_lat)); cor_lon = lon_min_ + j*dlon_; cor_lat = lat_min_ + (i+1)*dlat_; box_pairs_[idx].near_deg = std::min(box_pairs_[idx].near_deg, spherical_angle(inlon, inlat, cor_lon, cor_lat)); } } box_pairs_initalized_ = true; } else { int M, N; for (size_t i = 0; i < box_pairs_.size(); i++) { M = box_pairs_[i].id/lon_bsize_; N = box_pairs_[i].id%lon_bsize_; cor_lon = lon_min_ + N*dlon_; cor_lat = lat_min_ + M*dlat_; box_pairs_[i].near_deg = std::min(box_pairs_[i].near_deg, spherical_angle(inlon, inlat, cor_lon, cor_lat)); cor_lon = lon_min_ + (N+1)*dlon_; cor_lat = lat_min_ + M*dlat_; box_pairs_[i].near_deg = std::min(box_pairs_[i].near_deg, spherical_angle(inlon, inlat, cor_lon, cor_lat)); cor_lon = lon_min_ + (N+1)*dlon_; cor_lat = lat_min_ + (M+1)*dlat_; box_pairs_[i].near_deg = std::min(box_pairs_[i].near_deg, spherical_angle(inlon, inlat, cor_lon, cor_lat)); cor_lon = lon_min_ + N*dlon_; cor_lat = lat_min_ + (M+1)*dlat_; box_pairs_[i].near_deg = std::min(box_pairs_[i].near_deg, spherical_angle(inlon, inlat, cor_lon, cor_lat)); } } // Sort boxes_' pointers by the closest possible distance boxes_sorter_.execute(box_pairs_, [](array> &a, int l_id, int r_id)->bool{ if (a[l_id].near_deg < a[r_id].near_deg) return true; else return false; }); double deg, tmp_deg, maxi_deg = GCTL_BDL_MIN, maxi_record_deg = GCTL_BDL_MAX; T *tmp_item, *curr_item; LocalDeg_.resize(target_num); count = 0; for (size_t i = 0; i < box_pairs_.size(); i++) { if (!box_pairs_[i].box_ptr->empty() && box_pairs_[i].near_deg < maxi_record_deg) { for (size_t j = 0; j < box_pairs_[i].box_ptr->size(); j++) { deg = spherical_angle(inlon, inlat, box_pairs_[i].box_ptr->at(j).lon, box_pairs_[i].box_ptr->at(j).lat); curr_item = box_pairs_[i].box_ptr->at(j).item; if (count < target_num) { LocalDeg_[count] = deg; cargo_list[count] = curr_item; count++; maxi_deg = std::max(maxi_deg, deg); continue; } maxi_record_deg = maxi_deg; // 这比排序还要快 if (deg < maxi_record_deg) { for (size_t k = 0; k < target_num; k++) { if (deg < LocalDeg_[k]) { tmp_deg = LocalDeg_[k]; LocalDeg_[k] = deg; deg = tmp_deg; tmp_item = cargo_list[k]; cargo_list[k] = curr_item; curr_item = tmp_item; break; } } maxi_deg = GCTL_BDL_MIN; for (size_t k = 0; k < target_num; k++) { maxi_deg = std::max(maxi_deg, LocalDeg_[k]); } } } } } return; } template double boxes_sph::spherical_angle(double lon1, double lat1, double lon2, double lat2) { double x1, y1, z1, x2, y2, z2; x1 = cos(GCTL_Pi*lat1/180.0)*cos(GCTL_Pi*lon1/180.0); y1 = cos(GCTL_Pi*lat1/180.0)*sin(GCTL_Pi*lon1/180.0); z1 = sin(GCTL_Pi*lat1/180.0); x2 = cos(GCTL_Pi*lat2/180.0)*cos(GCTL_Pi*lon2/180.0); y2 = cos(GCTL_Pi*lat2/180.0)*sin(GCTL_Pi*lon2/180.0); z2 = sin(GCTL_Pi*lat2/180.0); return acos(std::max(-1.0, std::min(1.0, (x1*x2 + y1*y2 + z1*z2))))*180.0/GCTL_Pi; } } #endif // _BOXSORT_SPH_H