31 return (v[0], v[1] * cos - v[2] * sin, v[1] * sin + v[2] * cos)
33 return (v[0] * cos + v[2] * sin, v[1], -v[0] * sin + v[2] * cos)
35 return (v[0] * cos - v[1] * sin, v[0] * sin + v[1] * cos, v[2])
37 return _rotX(v, -sin, cos)
39 return _rotY(v, -sin, cos)
41 return _rotZ(v, -sin, cos)
45 """A quad-sphere based pixelization of the unit sphere. Each of the 6
46 root pixels are divided into an R by R grid of sky-pixels, where R
47 is the resolution (a positive integer). Sky-pixel identifiers are
48 contiguous integers in range [0, 6 * R**2), and are ordered such that
49 enumerating the pixels 0, 1, ..., 6 * R**2 results in a roughly spiral
50 traversal (from south to north pole) of the unit sphere. In particular,
51 iterating over pixels in this order guarantees that when arriving at
54 - all pixels with id P' < P have been visited
55 - all neighbors of pixels with id P' < P - 8R have been visited
57 Each pixel may optionally be expanded by a padding angle A, such that
58 points on the unit sphere outside of a pixel but within angular separation
59 A of the fiducial pixel boundaries are also considered to be part of that
60 pixel. Pixel (and padded pixel) edges are great circles on the unit
63 This class provides methods for computing fiducial or padded geometric
64 representations of a sky-pixel - spherical convex polygons in both
65 cases. It also supports finding the list of sky-pixels intersecting
66 an input spherical convex polygon, as well as determining the neighbors
67 and center of a sky-pixel.
69 TODO: Right now, pixels are defined by a simple tangent plane projection
70 of each cube face onto the sphere, but pixel boundaries could be adjusted
71 to produce equal area pixels.
74 """Creates a new quad-sphere sky pixelisation.
76 resolution: the number of pixels along the x and y axes of each root
79 paddingRad: the angular separation (rad) by which fiducial sky-pixels
82 if not isinstance(resolution, (int, long)):
84 'Quad-sphere resolution parameter must be an int or long')
85 if resolution < 3
or resolution > 16384:
87 'Quad-sphere resolution must be in range [3, 16384]')
88 if not isinstance(paddingRad, float):
90 'Quad-sphere pixel padding angle must be a float')
91 if paddingRad < 0.0
or paddingRad >= math.pi * 0.25:
93 'Quad-sphere pixel padding radius must be in range [0, 45) deg')
97 x, y, z = (1.0, 0.0, 0.0), (0.0, 1.0, 0.0), (0.0, 0.0, 1.0)
98 nx, ny, nz = (-1.0, 0.0, 0.0), (0.0, -1.0, 0.0), (0.0, 0.0, -1.0)
102 self.
x = [ny, y, nx, ny, x, ny]
104 self.
y = [x, z, z, z, z, nx]
106 self.
xrot = [_rotX, _rotZ, _rotZ, _rotZ, _rotZ, _rotNX]
108 self.
yrot = [_rotY, _rotNY, _rotX, _rotY, _rotNX, _rotY]
113 for root
in xrange(6):
116 c, x, y = self.
center[root], self.
x[root], self.
y[root]
117 for i
in xrange(R + 1):
120 f = 2.0 * float(i) / float(R) - 1.0
121 thetaX = math.radians(0.5 * geom.cartesianAngularSep(
122 (c[0] + f * x[0] + y[0],
123 c[1] + f * x[1] + y[1],
124 c[2] + f * x[2] + y[2]),
125 (c[0] + f * x[0] - y[0],
126 c[1] + f * x[1] - y[1],
127 c[2] + f * x[2] - y[2])))
128 thetaY = math.radians(0.5 * geom.cartesianAngularSep(
129 (c[0] + x[0] + f * y[0],
130 c[1] + x[1] + f * y[1],
131 c[2] + x[2] + f * y[2]),
132 (c[0] - x[0] + f * y[0],
133 c[1] - x[1] + f * y[1],
134 c[2] - x[2] + f * y[2])))
135 sinrx = sp / math.cos(thetaX)
136 sinry = sp / math.cos(thetaY)
137 cosrx = math.sqrt(1.0 - sinrx * sinrx)
138 cosry = math.sqrt(1.0 - sinry * sinry)
143 xlp = self.
xrot[root](xfp, sinrx, cosrx)
144 ybp = self.
yrot[root](yfp, sinry, cosry)
145 xlp = (-xlp[0], -xlp[1], -xlp[2])
146 ybp = (-ybp[0], -ybp[1], -ybp[2])
151 xrp = self.
xrot[root](xfp, -sinrx, cosrx)
152 ytp = self.
yrot[root](yfp, -sinry, cosry)
153 xplanes.append((xfp, xlp, xrp))
154 yplanes.append((yfp, ybp, ytp))
155 self.xplane.append(xplanes)
156 self.yplane.append(yplanes)
165 self.
id(2, R - 1, R - 1),
166 self.
id(2, R - 2, R - 1),
167 self.
id(3, 0, R - 1),
168 self.
id(3, 1, R - 1)),
170 (self.
id(0, 1, R - 1),
171 self.
id(0, 1, R - 2),
172 self.
id(0, 0, R - 2),
173 self.
id(1, R - 1, R - 1),
174 self.
id(1, R - 2, R - 1),
175 self.
id(2, 0, R - 1),
176 self.
id(2, 1, R - 1)),
178 (self.
id(0, R - 2, 0),
179 self.
id(0, R - 2, 1),
180 self.
id(0, R - 1, 1),
181 self.
id(3, R - 2, R - 1),
182 self.
id(3, R - 1, R - 1),
183 self.
id(4, 0, R - 1),
184 self.
id(4, 1, R - 1)),
186 (self.
id(0, R - 2, R - 1),
187 self.
id(0, R - 2, R - 2),
188 self.
id(0, R - 1, R - 2),
189 self.
id(1, 0, R - 1),
190 self.
id(1, 1, R - 1),
191 self.
id(4, R - 2, R - 1),
192 self.
id(4, R - 1, R - 1))
200 self.
id(4, R - 1, 0),
201 self.
id(4, R - 1, 1),
202 self.
id(5, R - 2, 0),
203 self.
id(5, R - 1, 0)),
205 (self.
id(1, 1, R - 1),
206 self.
id(1, 1, R - 2),
207 self.
id(1, 0, R - 2),
208 self.
id(4, R - 1, R - 2),
209 self.
id(4, R - 1, R - 1),
210 self.
id(0, R - 2, R - 1),
211 self.
id(0, R - 1, R - 1)),
213 (self.
id(1, R - 2, 0),
214 self.
id(1, R - 2, 1),
215 self.
id(1, R - 1, 1),
221 (self.
id(1, R - 2, R - 1),
222 self.
id(1, R - 2, R - 2),
223 self.
id(1, R - 1, R - 2),
224 self.
id(2, 0, R - 2),
225 self.
id(2, 0, R - 1),
226 self.
id(0, 0, R - 1),
227 self.
id(0, 1, R - 1))
235 self.
id(1, R - 1, 0),
236 self.
id(1, R - 1, 1),
240 (self.
id(2, 1, R - 1),
241 self.
id(2, 1, R - 2),
242 self.
id(2, 0, R - 2),
243 self.
id(1, R - 1, R - 2),
244 self.
id(1, R - 1, R - 1),
245 self.
id(0, 0, R - 2),
246 self.
id(0, 0, R - 1)),
248 (self.
id(2, R - 2, 0),
249 self.
id(2, R - 2, 1),
250 self.
id(2, R - 1, 1),
253 self.
id(5, 0, R - 2),
254 self.
id(5, 0, R - 1)),
256 (self.
id(2, R - 2, R - 1),
257 self.
id(2, R - 2, R - 2),
258 self.
id(2, R - 1, R - 2),
259 self.
id(3, 0, R - 2),
260 self.
id(3, 0, R - 1),
270 self.
id(2, R - 1, 0),
271 self.
id(2, R - 1, 1),
272 self.
id(5, 0, R - 1),
273 self.
id(5, 1, R - 1)),
275 (self.
id(3, 1, R - 1),
276 self.
id(3, 1, R - 2),
277 self.
id(3, 0, R - 2),
278 self.
id(2, R - 1, R - 2),
279 self.
id(2, R - 1, R - 1),
283 (self.
id(3, R - 2, 0),
284 self.
id(3, R - 2, 1),
285 self.
id(3, R - 1, 1),
288 self.
id(5, R - 2, R - 1),
289 self.
id(5, R - 1, R - 1)),
291 (self.
id(3, R - 2, R - 1),
292 self.
id(3, R - 2, R - 2),
293 self.
id(3, R - 1, R - 2),
294 self.
id(4, 0, R - 2),
295 self.
id(4, 0, R - 1),
296 self.
id(0, R - 2, 0),
297 self.
id(0, R - 1, 0))
305 self.
id(3, R - 1, 0),
306 self.
id(3, R - 1, 1),
307 self.
id(5, R - 1, R - 2),
308 self.
id(5, R - 1, R - 1)),
310 (self.
id(4, 1, R - 1),
311 self.
id(4, 1, R - 2),
312 self.
id(4, 0, R - 2),
313 self.
id(3, R - 1, R - 2),
314 self.
id(3, R - 1, R - 1),
315 self.
id(0, R - 1, 0),
316 self.
id(0, R - 1, 1)),
318 (self.
id(4, R - 2, 0),
319 self.
id(4, R - 2, 1),
320 self.
id(4, R - 1, 1),
323 self.
id(5, R - 1, 0),
324 self.
id(5, R - 1, 1)),
326 (self.
id(4, R - 2, R - 1),
327 self.
id(4, R - 2, R - 2),
328 self.
id(4, R - 1, R - 2),
329 self.
id(1, 0, R - 2),
330 self.
id(1, 0, R - 1),
331 self.
id(0, R - 1, R - 2),
332 self.
id(0, R - 1, R - 1))
340 self.
id(1, R - 2, 0),
341 self.
id(1, R - 1, 0),
345 (self.
id(5, 1, R - 1),
346 self.
id(5, 1, R - 2),
347 self.
id(5, 0, R - 2),
348 self.
id(2, R - 2, 0),
349 self.
id(2, R - 1, 0),
353 (self.
id(5, R - 2, 0),
354 self.
id(5, R - 2, 1),
355 self.
id(5, R - 1, 1),
358 self.
id(4, R - 2, 0),
359 self.
id(4, R - 1, 0)),
361 (self.
id(5, R - 2, R - 1),
362 self.
id(5, R - 2, R - 2),
363 self.
id(5, R - 1, R - 2),
364 self.
id(3, R - 2, 0),
365 self.
id(3, R - 1, 0),
372 """Returns a spherical convex polygon corresponding to the fiducial
373 or padded boundaries of the sky-pixel with the specified id.
375 root, ix, iy = self.
coords(pixelId)
376 xl = 2.0 * float(ix) / float(self.
resolution) - 1.0
377 xr = 2.0 * float(ix + 1) / float(self.
resolution) - 1.0
378 yb = 2.0 * float(iy) / float(self.
resolution) - 1.0
379 yt = 2.0 * float(iy + 1) / float(self.
resolution) - 1.0
380 c, x, y = self.
center[root], self.
x[root], self.
y[root]
381 v = map(geom.normalize, [(c[0] + xl * x[0] + yb * y[0],
382 c[1] + xl * x[1] + yb * y[1],
383 c[2] + xl * x[2] + yb * y[2]),
384 (c[0] + xr * x[0] + yb * y[0],
385 c[1] + xr * x[1] + yb * y[1],
386 c[2] + xr * x[2] + yb * y[2]),
387 (c[0] + xr * x[0] + yt * y[0],
388 c[1] + xr * x[1] + yt * y[1],
389 c[2] + xr * x[2] + yt * y[2]),
390 (c[0] + xl * x[0] + yt * y[0],
391 c[1] + xl * x[1] + yt * y[1],
392 c[2] + xl * x[2] + yt * y[2])])
393 if not fiducial
and self.
padding > 0.0:
396 theta = map(
lambda x: 0.5 * geom.cartesianAngularSep(x[0], x[1]),
397 ((v[0],v[3]), (v[1],v[0]), (v[2],v[1]), (v[3],v[2])))
398 sina = map(
lambda x: sp / math.cos(math.radians(x)), theta)
399 cosa = map(
lambda x: math.sqrt(1.0 - x * x), sina)
401 xlp = self.
xplane[root][ix][0]
402 ybp = self.
yplane[root][iy][0]
403 xrp = self.
xplane[root][ix + 1][0]
404 ytp = self.
yplane[root][iy + 1][0]
406 xlp = self.
xrot[root](xlp, -sina[0], cosa[0])
407 ybp = self.
yrot[root](ybp, -sina[1], cosa[1])
408 xrp = self.
xrot[root](xrp, sina[2], cosa[2])
409 ytp = self.
yrot[root](ytp, sina[3], cosa[3])
411 v = map(geom.normalize, [geom.cross(xlp, ybp),
412 geom.cross(xrp, ybp),
413 geom.cross(xrp, ytp),
414 geom.cross(xlp, ytp)])
415 return geom.SphericalConvexPolygon(v)
418 """Returns the center of a sky-pixel as a unit cartesian 3-vector.
420 root, ix, iy = self.
coords(pixelId)
421 xc = 2.0 * (float(ix) + 0.5) / float(self.
resolution) - 1.0
422 yc = 2.0 * (float(iy) + 0.5) / float(self.
resolution) - 1.0
423 c, x, y = self.
center[root], self.
x[root], self.
y[root]
424 return geom.normalize((c[0] + xc * x[0] + yc * y[0],
425 c[1] + xc * x[1] + yc * y[1],
426 c[2] + xc * x[2] + yc * y[2]))
429 """Returns a list of ids for sky-pixels adjacent to specified pixel.
431 root, ix, iy = self.
coords(pixelId)
440 neighbors = (self.
id(2, R - iy - 2, R - 1),
441 self.
id(2, R - iy - 1, R - 1),
442 self.
id(2, R - iy, R - 1))
444 neighbors = (self.
id(2, iy - 1, 0),
446 self.
id(2, iy + 1, 0))
450 neighbors = (self.
id(r, R - 1, iy - 1),
451 self.
id(r, R - 1, iy ),
452 self.
id(r, R - 1, iy + 1))
453 return neighbors + (self.
id(root, 1, iy - 1),
454 self.
id(root, 1, iy ),
455 self.
id(root, 1, iy + 1),
456 self.
id(root, 0, iy - 1),
457 self.
id(root, 0, iy + 1))
465 neighbors = (self.
id(4, iy - 1, R - 1),
466 self.
id(4, iy, R - 1),
467 self.
id(4, iy + 1, R - 1))
469 neighbors = (self.
id(4, R - iy - 2, 0),
470 self.
id(4, R - iy - 1, 0),
471 self.
id(4, R - iy, 0))
475 neighbors = (self.
id(r, 0, iy - 1),
477 self.
id(r, 0, iy + 1))
478 return neighbors + (self.
id(root, R - 2, iy - 1),
479 self.
id(root, R - 2, iy),
480 self.
id(root, R - 2, iy + 1),
481 self.
id(root, R - 1, iy - 1),
482 self.
id(root, R - 1, iy + 1))
486 neighbors = (self.
id(3, ix - 1, R - 1),
487 self.
id(3, ix , R - 1),
488 self.
id(3, ix + 1, R - 1))
490 neighbors = (self.
id(5, R - ix - 2, 0),
491 self.
id(5, R - ix - 1, 0),
492 self.
id(5, R - ix, 0))
494 neighbors = (self.
id(5, 0, ix - 1),
496 self.
id(5, 0, ix + 1))
498 neighbors = (self.
id(5, ix - 1, R - 1),
499 self.
id(5, ix , R - 1),
500 self.
id(5, ix + 1, R - 1))
502 neighbors = (self.
id(5, R - 1, R - ix - 2),
503 self.
id(5, R - 1, R - ix - 1),
504 self.
id(5, R - 1, R - ix ))
506 neighbors = (self.
id(1, R - ix - 2, 0),
507 self.
id(1, R - ix - 1, 0),
508 self.
id(1, R - ix, 0))
509 return neighbors + (self.
id(root, ix - 1, 1),
510 self.
id(root, ix, 1),
511 self.
id(root, ix + 1, 1),
512 self.
id(root, ix - 1, 0),
513 self.
id(root, ix + 1, 0))
517 neighbors = (self.
id(1, R - ix - 2, R - 1),
518 self.
id(1, R - ix - 1, R - 1),
519 self.
id(1, R - ix, R - 1))
521 neighbors = (self.
id(0, R - ix - 2, R - 1),
522 self.
id(0, R - ix - 1, R - 1),
523 self.
id(0, R - ix, R - 1))
525 neighbors = (self.
id(0, 0, R - ix - 2),
526 self.
id(0, 0, R - ix - 1),
527 self.
id(0, 0, R - ix ))
529 neighbors = (self.
id(0, ix - 1, 0),
531 self.
id(0, ix + 1, 0))
533 neighbors = (self.
id(0, R - 1, ix - 1),
534 self.
id(0, R - 1, ix ),
535 self.
id(0, R - 1, ix + 1))
537 neighbors = (self.
id(3, ix - 1, 0),
539 self.
id(3, ix + 1, 0))
540 return neighbors + (self.
id(root, ix - 1, R - 2),
541 self.
id(root, ix, R - 2),
542 self.
id(root, ix + 1, R - 2),
543 self.
id(root, ix - 1, R - 1),
544 self.
id(root, ix + 1, R - 1))
546 return (self.
id(root, ix - 1, iy - 1),
547 self.
id(root, ix , iy - 1),
548 self.
id(root, ix + 1, iy - 1),
549 self.
id(root, ix - 1, iy),
550 self.
id(root, ix + 1, iy),
551 self.
id(root, ix - 1, iy + 1),
552 self.
id(root, ix , iy + 1),
553 self.
id(root, ix + 1, iy + 1))
556 """Returns a list of ids for sky-pixels intersecting the given
557 polygon. Intersection is computed against padded sky-pixels.
559 if not isinstance(polygon, geom.SphericalConvexPolygon):
560 raise TypeError(
'Expecting a SphericalConvexPolygon as input ' +
561 'to the sky-pixel intersection computation')
564 for root
in xrange(6):
567 for p
in (self.
xplane[root][0][2], self.
xplane[root][R][1],
575 self.
_intersect(pixels, poly, root, (0, R, 0, R))
579 """Returns the total number of sky-pixels in this pixelization.
584 """Returns an iterator over the sky-pixel ids of all the pixels
585 in this pixelization.
587 return iter(xrange(len(self)))
590 return ''.join([self.__class__.__name__,
'(',
594 """Maps from spherical coordinates (longitude and latitude
595 angles theta and phi, both in radians) to a sky-pixel id.
598 root = int(math.fmod(0.5 + 2.0 * theta / math.pi, 4.0))
599 theta1 = theta - 0.5 * math.pi * root
601 tanPhi = math.tan(phi)
602 y = tanPhi / math.cos(theta1)
605 x = -math.sin(theta) / tanPhi
606 y = math.cos(theta) / tanPhi
609 x = math.sin(theta) / tanPhi
610 y = math.cos(theta) / tanPhi
613 x = int(R * 0.5 * (x + 1.0))
614 y = int(R * 0.5 * (y + 1.0))
619 return self.
id(root, x, y)
621 def id(self, root, x, y):
622 """Maps from a root pixel number and x, y pixel coordinates
625 if not all(isinstance(p, (int, long))
for p
in (root, x, y)):
626 raise TypeError(
'Pixel coordinates must all be of type int or long')
627 if root < 0
or root > 5:
628 raise RuntimeError(
'root pixel number must be in range [0,6)')
630 if x < 0
or x >= R
or y < 0
or y >= R:
631 raise RuntimeError(
'x and y pixel coordinates must ' +
632 'be in range [0,%d)' % R)
633 if root > 0
and root < 5:
635 return R ** 2 + 4 * R * y + (root - 1) * R + x
637 dx = x - 0.5 * (R - 1)
638 dy = y - 0.5 * (R - 1)
639 ring = max(abs(dx), abs(dy))
645 return 6 * R ** 2 - 1
647 i = int(r * r + ring - dx)
649 i = int(r * r + 3.0 * ring + dy)
651 i = int(r * r + 5.0 * ring + dx)
653 i = int(r * r + 7.0 * ring - dy)
659 return 6 * R ** 2 - i - 1
662 """Maps from a sky-pixel id to a root pixel number and x, y
665 if not isinstance(pixelId, (int, long)):
667 'Sky-pixel id must be an int or long')
670 if pixelId < 0
or pixelId >= 6 * R2:
672 'Invalid sky-pixel id %d - value must be in range [0, %d)' %
678 y = pixelId / (4 * R)
680 root = 1 + pixelId / R
681 x = pixelId - (root - 1) * R
686 i = 6 * R2 - pixelId - 1
690 s = int(math.floor(math.sqrt(i)))
692 ring = 0.5 * (s + ((s & 1) ^ 1))
694 ring = 0.5 * (s + (s & 1))
697 return (root, R / 2, R / 2)
702 elif i <= 4.0 * ring:
705 elif i <= 6.0 * ring:
711 return (root, int(dx + 0.5 * (R - 1)), int(dy + 0.5 * (R - 1)))
714 assert isinstance(ix, (int,long))
and ix >= 0
and ix <= self.
resolution
715 x = 2.0 * float(ix) / float(self.
resolution) - 1.0
716 c, b = self.
center[root], self.
x[root]
717 v = (c[0] + x * b[0], c[1] + x * b[1], c[2] + x * b[2])
718 return geom.normalize(self.
xrot[root](v, 1.0, 0.0))
721 assert isinstance(iy, (int,long))
and iy >= 0
and iy <= self.
resolution
722 y = 2.0 * float(iy) / float(self.
resolution) - 1.0
723 c, b = self.
center[root], self.
y[root]
724 v = (c[0] + y * b[0], c[1] + y * b[1], c[2] + y * b[2])
725 return geom.normalize(self.
yrot[root](v, 1.0, 0.0))
730 if dx == 1
and dy == 1:
731 i = self.
id(root, box[0], box[2])
737 xsplit = box[0] + dx / 2
738 p = poly.clip(self.
xplane[root][xsplit][1])
740 self.
_intersect(pixels, p, root, (box[0], xsplit, box[2], box[3]))
741 p = poly.clip(self.
xplane[root][xsplit][2])
743 self.
_intersect(pixels, p, root, (xsplit, box[1], box[2], box[3]))
746 ysplit = box[2] + dy / 2
747 p = poly.clip(self.
yplane[root][ysplit][1])
749 self.
_intersect(pixels, p, root, (box[0], box[1], box[2], ysplit))
750 p = poly.clip(self.
yplane[root][ysplit][2])
752 self.
_intersect(pixels, p, root, (box[0], box[1], ysplit, box[3]))
757 "skypix",
"QuadSpherePixelizationDictionary.paf",
"policy")
758 defaults = pexPolicy.Policy.createPolicy(
759 policyFile, policyFile.getRepositoryPath())
762 policy.mergeDefaults(defaults)
764 resolution = policy.get(
'resolutionPix')
765 padding = math.radians(policy.get(
'paddingArcsec') / 3600.0)
769 """Computes and returns a spherical convex polygon approximation to the
770 region of the unit sphere covered by an image specified with a WCS and
771 a width/height (pixels). The polygon is computed by connecting the
772 world coordinates of the 4 image corners with great circles, and can
773 optionally be padded by padRad radians.
776 cd = wcs.getCDMatrix()
777 xpad = math.degrees(padRad) / math.sqrt(cd[0,0]**2 + cd[0,1]**2)
778 ypad = math.degrees(padRad) / math.sqrt(cd[1,0]**2 + cd[1,1]**2)
779 xmin, ymin = -0.5 - xpad, -0.5 - ypad
780 xmax, ymax = widthPix + xpad - 0.5, heightPix + ypad - 0.5
782 coords = [wcs.pixelToSky(xmin, ymin), wcs.pixelToSky(xmax, ymin),
783 wcs.pixelToSky(xmax, ymax), wcs.pixelToSky(xmin, ymax)]
787 verts.append(tuple(c.getVector()))
789 convex, cc = geom.convex(verts)
791 raise RuntimeError(
'Image corners do not form a convex polygon: ' + cc)
794 return geom.SphericalConvexPolygon(verts)
bool all(CoordinateExpr< N > const &expr)
Return true if all elements are true.
a container for holding hierarchical configuration data in memory.
a representation of a default Policy file that is stored as a file in the installation directory of a...
def createQuadSpherePixelization