LSSTApplications  19.0.0-14-gb0260a2+72efe9b372,20.0.0+7927753e06,20.0.0+8829bf0056,20.0.0+995114c5d2,20.0.0+b6f4b2abd1,20.0.0+bddc4f4cbe,20.0.0-1-g253301a+8829bf0056,20.0.0-1-g2b7511a+0d71a2d77f,20.0.0-1-g5b95a8c+7461dd0434,20.0.0-12-g321c96ea+23efe4bbff,20.0.0-16-gfab17e72e+fdf35455f6,20.0.0-2-g0070d88+ba3ffc8f0b,20.0.0-2-g4dae9ad+ee58a624b3,20.0.0-2-g61b8584+5d3db074ba,20.0.0-2-gb780d76+d529cf1a41,20.0.0-2-ged6426c+226a441f5f,20.0.0-2-gf072044+8829bf0056,20.0.0-2-gf1f7952+ee58a624b3,20.0.0-20-geae50cf+e37fec0aee,20.0.0-25-g3dcad98+544a109665,20.0.0-25-g5eafb0f+ee58a624b3,20.0.0-27-g64178ef+f1f297b00a,20.0.0-3-g4cc78c6+e0676b0dc8,20.0.0-3-g8f21e14+4fd2c12c9a,20.0.0-3-gbd60e8c+187b78b4b8,20.0.0-3-gbecbe05+48431fa087,20.0.0-38-ge4adf513+a12e1f8e37,20.0.0-4-g97dc21a+544a109665,20.0.0-4-gb4befbc+087873070b,20.0.0-4-gf910f65+5d3db074ba,20.0.0-5-gdfe0fee+199202a608,20.0.0-5-gfbfe500+d529cf1a41,20.0.0-6-g64f541c+d529cf1a41,20.0.0-6-g9a5b7a1+a1cd37312e,20.0.0-68-ga3f3dda+5fca18c6a4,20.0.0-9-g4aef684+e18322736b,w.2020.45
LSSTDataManagementBasePackage
Chunker.cc
Go to the documentation of this file.
1 /*
2  * LSST Data Management System
3  * Copyright 2014-2015 AURA/LSST.
4  *
5  * This product includes software developed by the
6  * LSST Project (http://www.lsst.org/).
7  *
8  * This program is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the LSST License Statement and
19  * the GNU General Public License along with this program. If not,
20  * see <https://www.lsstcorp.org/LegalNotices/>.
21  */
22 
25 
26 #include "lsst/sphgeom/Chunker.h"
27 
28 namespace lsst {
29 namespace sphgeom {
30 
31 namespace {
32 
33 int32_t computeNumSegments(AngleInterval const & latitudes, Angle width) {
34  // TODO(smm): Document this.
35  if (width.asRadians() > PI) {
36  return 1;
37  }
38  Angle maxAbsLat = std::max(abs(latitudes.getA()), abs(latitudes.getB()));
39  if (maxAbsLat.asRadians() > 0.5 * PI - 4.85e-6) {
40  return 1;
41  }
42  double cosWidth = cos(width);
43  double sinLat = sin(maxAbsLat);
44  double cosLat = cos(maxAbsLat);
45  double x = cosWidth - sinLat * sinLat;
46  double u = cosLat * cosLat;
47  double y = std::sqrt(std::fabs(u * u - x * x));
48  return static_cast<int32_t>(
49  std::floor(2.0 * PI / std::fabs(std::atan2(y, x))));
50 }
51 
52 constexpr double BOX_EPSILON = 5.0e-12; // ~1 micro-arcsecond
53 
54 } // unnamed namespace
55 
56 
57 Chunker::Chunker(int32_t numStripes,
58  int32_t numSubStripesPerStripe) :
59  _numStripes(numStripes),
60  _numSubStripesPerStripe(numSubStripesPerStripe),
61  _numSubStripes(numStripes * numSubStripesPerStripe),
62  _maxSubChunksPerSubStripeChunk(0),
63  _subStripeHeight(Angle(PI) / _numSubStripes)
64 {
65  if (numStripes < 1 || numSubStripesPerStripe < 1) {
66  throw std::runtime_error("The number of stripes and sub-stripes "
67  "per stripe must be positive");
68  }
69  if (numStripes * numSubStripesPerStripe > 180*3600) {
70  throw std::runtime_error("Sub-stripes are too small");
71  }
72  Angle const stripeHeight = Angle(PI) / _numStripes;
73  _stripes.reserve(_numStripes);
74  _subStripes.reserve(_numSubStripes);
75  for (int32_t s = 0; s < _numStripes; ++s) {
76  // Compute stripe latitude bounds.
77  AngleInterval sLat(s * stripeHeight - Angle(0.5 * PI),
78  (s + 1) * stripeHeight - Angle(0.5 * PI));
79  Stripe stripe;
80  int32_t const nc = computeNumSegments(sLat, stripeHeight);
81  stripe.chunkWidth = Angle(2.0 * PI) / nc;
82  stripe.numChunksPerStripe = nc;
83  int32_t ss = s * _numSubStripesPerStripe;
84  int32_t const ssEnd = ss + _numSubStripesPerStripe;
85  for (; ss < ssEnd; ++ss) {
86  // Compute sub-stripe latitude bounds.
87  AngleInterval ssLat(ss * _subStripeHeight - Angle(0.5 * PI),
88  (ss + 1) * _subStripeHeight - Angle(0.5 * PI));
89  SubStripe subStripe;
90  int32_t const nsc = computeNumSegments(ssLat, _subStripeHeight) / nc;
91  stripe.numSubChunksPerChunk += nsc;
92  subStripe.numSubChunksPerChunk = nsc;
93  if (nsc > _maxSubChunksPerSubStripeChunk) {
94  _maxSubChunksPerSubStripeChunk = nsc;
95  }
96  subStripe.subChunkWidth = Angle(2.0 * PI) / (nsc * nc);
97  _subStripes.push_back(subStripe);
98  }
99  _stripes.push_back(stripe);
100  }
101 }
102 
104  std::vector<int32_t> chunkIds;
105  // Find the stripes that intersect the bounding box of r.
106  Box b = r.getBoundingBox().dilatedBy(Angle(BOX_EPSILON));
107  double ya = std::floor((b.getLat().getA() + Angle(0.5 * PI)) / _subStripeHeight);
108  double yb = std::floor((b.getLat().getB() + Angle(0.5 * PI)) / _subStripeHeight);
109  int32_t minSS = std::min(static_cast<int32_t>(ya), _numSubStripes - 1);
110  int32_t maxSS = std::min(static_cast<int32_t>(yb), _numSubStripes - 1);
111  int32_t minS = minSS / _numSubStripesPerStripe;
112  int32_t maxS = maxSS / _numSubStripesPerStripe;
113  for (int32_t s = minS; s <= maxS; ++s) {
114  // Find the chunks of s that intersect the bounding box of r.
115  Angle chunkWidth = _stripes[s].chunkWidth;
116  int32_t nc = _stripes[s].numChunksPerStripe;
117  double xa = std::floor(b.getLon().getA() / chunkWidth);
118  double xb = std::floor(b.getLon().getB() / chunkWidth);
119  int32_t ca = std::min(static_cast<int32_t>(xa), nc - 1);
120  int32_t cb = std::min(static_cast<int32_t>(xb), nc - 1);
121  if (ca == cb && b.getLon().wraps()) {
122  ca = 0;
123  cb = nc - 1;
124  }
125  // Examine each chunk overlapping the bounding box of r.
126  if (ca <= cb) {
127  for (int32_t c = ca; c <= cb; ++c) {
128  if ((r.relate(getChunkBoundingBox(s, c)) & DISJOINT) == 0) {
129  chunkIds.push_back(_getChunkId(s, c));
130  }
131  }
132  } else {
133  for (int32_t c = 0; c <= cb; ++c) {
134  if ((r.relate(getChunkBoundingBox(s, c)) & DISJOINT) == 0) {
135  chunkIds.push_back(_getChunkId(s, c));
136  }
137  }
138  for (int32_t c = ca; c < nc; ++c) {
139  if ((r.relate(getChunkBoundingBox(s, c)) & DISJOINT) == 0) {
140  chunkIds.push_back(_getChunkId(s, c));
141  }
142  }
143  }
144  }
145  return chunkIds;
146 }
147 
149  Region const & r) const
150 {
151  std::vector<SubChunks> chunks;
152  // Find the stripes that intersect the bounding box of r.
153  Box b = r.getBoundingBox().dilatedBy(Angle(BOX_EPSILON));
154  double ya = std::floor((b.getLat().getA() + Angle(0.5 * PI)) / _subStripeHeight);
155  double yb = std::floor((b.getLat().getB() + Angle(0.5 * PI)) / _subStripeHeight);
156  int32_t minSS = std::min(static_cast<int32_t>(ya), _numSubStripes - 1);
157  int32_t maxSS = std::min(static_cast<int32_t>(yb), _numSubStripes - 1);
158  int32_t minS = minSS / _numSubStripesPerStripe;
159  int32_t maxS = maxSS / _numSubStripesPerStripe;
160  for (int32_t s = minS; s <= maxS; ++s) {
161  // Find the chunks of s that intersect the bounding box of r.
162  Angle chunkWidth = _stripes[s].chunkWidth;
163  int32_t nc = _stripes[s].numChunksPerStripe;
164  double xa = std::floor(b.getLon().getA() / chunkWidth);
165  double xb = std::floor(b.getLon().getB() / chunkWidth);
166  int32_t ca = std::min(static_cast<int32_t>(xa), nc - 1);
167  int32_t cb = std::min(static_cast<int32_t>(xb), nc - 1);
168  if (ca == cb && b.getLon().wraps()) {
169  ca = 0;
170  cb = nc - 1;
171  }
172  // Examine sub-chunks for each chunk overlapping the bounding box of r.
173  if (ca <= cb) {
174  for (int32_t c = ca; c <= cb; ++c) {
175  _getSubChunks(chunks, r, b.getLon(), s, c, minSS, maxSS);
176  }
177  } else {
178  for (int32_t c = 0; c <= cb; ++c) {
179  _getSubChunks(chunks, r, b.getLon(), s, c, minSS, maxSS);
180  }
181  for (int32_t c = ca; c < nc; ++c) {
182  _getSubChunks(chunks, r, b.getLon(), s, c, minSS, maxSS);
183  }
184  }
185  }
186  return chunks;
187 }
188 
189 void Chunker::_getSubChunks(std::vector<SubChunks> & chunks,
190  Region const & r,
191  NormalizedAngleInterval const & lon,
192  int32_t stripe,
193  int32_t chunk,
194  int32_t minSS,
195  int32_t maxSS) const
196 {
197  SubChunks subChunks;
198  subChunks.chunkId = _getChunkId(stripe, chunk);
199  if ((r.relate(getChunkBoundingBox(stripe, chunk)) & CONTAINS) != 0) {
200  // r contains the entire chunk, so there is no need to test sub-chunks
201  // for intersection with r.
202  subChunks.subChunkIds = getAllSubChunks(subChunks.chunkId);
203  } else {
204  // Find the sub-stripes to iterate over.
205  minSS = std::max(minSS, stripe * _numSubStripesPerStripe);
206  maxSS = std::min(maxSS, (stripe + 1) * _numSubStripesPerStripe - 1);
207  int32_t const nc = _stripes[stripe].numChunksPerStripe;
208  for (int32_t ss = minSS; ss <= maxSS; ++ss) {
209  // Find the sub-chunks of ss to iterate over.
210  Angle subChunkWidth = _subStripes[ss].subChunkWidth;
211  int32_t const nsc = _subStripes[ss].numSubChunksPerChunk;
212  double xa = std::floor(lon.getA() / subChunkWidth);
213  double xb = std::floor(lon.getB() / subChunkWidth);
214  int32_t sca = std::min(static_cast<int32_t>(xa), nc * nsc - 1);
215  int32_t scb = std::min(static_cast<int32_t>(xb), nc * nsc - 1);
216  if (sca == scb && lon.wraps()) {
217  sca = 0;
218  scb = nc * nsc - 1;
219  }
220  int32_t minSC = chunk * nsc;
221  int32_t maxSC = (chunk + 1) * nsc - 1;
222  // Test each sub-chunk against r, and record those that intersect.
223  if (sca <= scb) {
224  minSC = std::max(sca, minSC);
225  maxSC = std::min(scb, maxSC);
226  for (int32_t sc = minSC; sc <= maxSC; ++sc) {
227  if ((r.relate(getSubChunkBoundingBox(ss, sc)) & DISJOINT) == 0) {
228  subChunks.subChunkIds.push_back(
229  _getSubChunkId(stripe, ss, chunk, sc));
230  }
231  }
232  } else {
233  sca = std::max(sca, minSC);
234  scb = std::min(scb, maxSC);
235  for (int32_t sc = sca; sc <= maxSC; ++sc) {
236  if ((r.relate(getSubChunkBoundingBox(ss, sc)) & DISJOINT) == 0) {
237  subChunks.subChunkIds.push_back(
238  _getSubChunkId(stripe, ss, chunk, sc));
239  }
240  }
241  for (int32_t sc = minSC; sc <= scb; ++sc) {
242  if ((r.relate(getSubChunkBoundingBox(ss, sc)) & DISJOINT) == 0) {
243  subChunks.subChunkIds.push_back(
244  _getSubChunkId(stripe, ss, chunk, sc));
245  }
246  }
247  }
248  }
249  }
250  // If any sub-chunks of this chunk intersect r,
251  // append them to the result vector.
252  if (!subChunks.subChunkIds.empty()) {
253  chunks.push_back(SubChunks());
254  chunks.back().swap(subChunks);
255  }
256 }
257 
259  std::vector<int32_t> chunkIds;
260  for (int32_t s = 0; s < _numStripes; ++s) {
261  int32_t nc = _stripes[s].numChunksPerStripe;
262  for (int32_t c = 0; c < nc; ++c) {
263  chunkIds.push_back(_getChunkId(s, c));
264  }
265  }
266  return chunkIds;
267 }
268 
270  std::vector<int32_t> subChunkIds;
271  int32_t s = getStripe(chunkId);
272  subChunkIds.reserve(_stripes.at(s).numSubChunksPerChunk);
273  int32_t const ssBeg = s * _numSubStripesPerStripe;
274  int32_t const ssEnd = ssBeg + _numSubStripesPerStripe;
275  for (int32_t ss = ssBeg; ss < ssEnd; ++ss) {
276  int32_t const scEnd = _subStripes[ss].numSubChunksPerChunk;
277  int32_t const subChunkIdBase = _maxSubChunksPerSubStripeChunk * (ss - ssBeg);
278  for (int32_t sc = 0; sc < scEnd; ++sc) {
279  subChunkIds.push_back(subChunkIdBase + sc);
280  }
281  }
282  return subChunkIds;
283 }
284 
285 bool Chunker::valid(int32_t chunkId) const {
286  int32_t const s = getStripe(chunkId);
287  return s >= 0 and s < _numStripes and
288  getChunk(chunkId, s) < _stripes.at(s).numChunksPerStripe;
289 }
290 
291 Box Chunker::getChunkBoundingBox(int32_t stripe, int32_t chunk) const {
292  Angle chunkWidth = _stripes[stripe].chunkWidth;
293  NormalizedAngleInterval lon(chunkWidth * chunk,
294  chunkWidth * (chunk + 1));
295  int32_t ss = stripe * _numSubStripesPerStripe;
296  int32_t ssEnd = ss + _numSubStripesPerStripe;
297  AngleInterval lat(ss * _subStripeHeight - Angle(0.5 * PI),
298  ssEnd * _subStripeHeight - Angle(0.5 * PI));
299  return Box(lon, lat).dilatedBy(Angle(BOX_EPSILON));
300 }
301 
302 Box Chunker::getSubChunkBoundingBox(int32_t subStripe, int32_t subChunk) const {
303  Angle subChunkWidth = _subStripes[subStripe].subChunkWidth;
304  NormalizedAngleInterval lon(subChunkWidth * subChunk,
305  subChunkWidth * (subChunk + 1));
306  AngleInterval lat(subStripe * _subStripeHeight - Angle(0.5 * PI),
307  (subStripe + 1) * _subStripeHeight - Angle(0.5 * PI));
308  return Box(lon, lat).dilatedBy(Angle(BOX_EPSILON));
309 }
310 
311 }} // namespace lsst::sphgeom
y
int y
Definition: SpanSet.cc:49
lsst::sphgeom::Chunker::getChunksIntersecting
std::vector< int32_t > getChunksIntersecting(Region const &r) const
getChunksIntersecting returns all the chunks that potentially intersect the given region.
Definition: Chunker.cc:103
lsst::sphgeom::NormalizedAngleInterval
NormalizedAngleInterval represents closed intervals of normalized angles, i.e.
Definition: NormalizedAngleInterval.h:57
std::floor
T floor(T... args)
lsst::sphgeom::Region::relate
virtual Relationship relate(Region const &) const =0
lsst::sphgeom::sin
double sin(Angle const &a)
Definition: Angle.h:102
lsst::sphgeom::PI
constexpr double PI
Definition: constants.h:36
std::fabs
T fabs(T... args)
lsst::sphgeom::Region
Region is a minimal interface for 2-dimensional regions on the unit sphere.
Definition: Region.h:79
std::atan2
T atan2(T... args)
lsst::sphgeom::Box::dilatedBy
Box dilatedBy(Angle r) const
Definition: Box.h:273
Chunker.h
This file declares a class for partitioning the sky into chunks and sub-chunks.
std::vector::reserve
T reserve(T... args)
std::vector< int32_t >
lsst::sphgeom::abs
Angle abs(Angle const &a)
Definition: Angle.h:106
lsst::sphgeom::Box
Box represents a rectangle in spherical coordinate space that contains its boundary.
Definition: Box.h:54
std::vector::back
T back(T... args)
std::sqrt
T sqrt(T... args)
lsst::sphgeom::Chunker::valid
bool valid(int32_t chunkId) const
Return 'true' if the specified chunk number is valid.
Definition: Chunker.cc:285
lsst::sphgeom::SubChunks::chunkId
int32_t chunkId
Definition: Chunker.h:44
lsst::sphgeom::Chunker::getAllSubChunks
std::vector< int32_t > getAllSubChunks(int32_t chunkId) const
getAllSubChunks returns the complete set of sub-chunk IDs for the given chunk.
Definition: Chunker.cc:269
std::vector::push_back
T push_back(T... args)
lsst::sphgeom::NormalizedAngleInterval::wraps
bool wraps() const
wraps returns true if the interval "wraps" around the 0/2π angle discontinuity, i....
Definition: NormalizedAngleInterval.h:135
lsst::sphgeom::SubChunks
SubChunks represents a set of sub-chunks of a particular chunk.
Definition: Chunker.h:43
lsst::afw::table::Angle
lsst::geom::Angle Angle
Definition: misc.h:33
lsst::sphgeom::Chunker::getSubChunkBoundingBox
Box getSubChunkBoundingBox(int32_t subStripe, int32_t subChunk) const
Definition: Chunker.cc:302
std::vector::at
T at(T... args)
x
double x
Definition: ChebyshevBoundedField.cc:277
lsst::sphgeom::Chunker::getChunk
int32_t getChunk(int32_t chunkId, int32_t stripe) const
Return the chunk for the given chunkId and stripe.
Definition: Chunker.h:121
lsst::sphgeom::Chunker::getAllChunks
std::vector< int32_t > getAllChunks() const
getAllChunks returns the complete set of chunk IDs for the unit sphere.
Definition: Chunker.cc:258
lsst::sphgeom::Chunker::getStripe
int32_t getStripe(int32_t chunkId) const
Return the stripe for the specified chunkId.
Definition: Chunker.h:116
std::runtime_error
STL class.
lsst::sphgeom::Chunker::getChunkBoundingBox
Box getChunkBoundingBox(int32_t stripe, int32_t chunk) const
Definition: Chunker.cc:291
lsst::sphgeom::AngleInterval
AngleInterval represents closed intervals of arbitrary angles.
Definition: AngleInterval.h:39
b
table::Key< int > b
Definition: TransmissionCurve.cc:467
lsst
A base class for image defects.
Definition: imageAlgorithm.dox:1
std::min
T min(T... args)
lsst::sphgeom::Chunker::Chunker
Chunker(int32_t numStripes, int32_t numSubStripesPerStripe)
Definition: Chunker.cc:57
lsst::sphgeom::Chunker::getSubChunksIntersecting
std::vector< SubChunks > getSubChunksIntersecting(Region const &r) const
getSubChunksIntersecting returns all the sub-chunks that potentially intersect the given region.
Definition: Chunker.cc:148
lsst::sphgeom::SubChunks::subChunkIds
std::vector< int32_t > subChunkIds
Definition: Chunker.h:45
lsst::sphgeom::NormalizedAngleInterval::getA
NormalizedAngle getA() const
getA returns the first endpoint of this interval.
Definition: NormalizedAngleInterval.h:119
std::vector::empty
T empty(T... args)
lsst::sphgeom::Region::getBoundingBox
virtual Box getBoundingBox() const =0
getBoundingBox returns a bounding-box for this region.
lsst::sphgeom::Angle
Angle represents an angle in radians.
Definition: Angle.h:43
std::max
T max(T... args)
lsst::sphgeom::cos
double cos(Angle const &a)
Definition: Angle.h:103
lsst::sphgeom::NormalizedAngleInterval::getB
NormalizedAngle getB() const
getB returns the second endpoint of this interval.
Definition: NormalizedAngleInterval.h:122