Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

# 

# LSST Data Management System 

# Copyright 2008-2015 AURA/LSST. 

# 

# This product includes software developed by the 

# LSST Project (http://www.lsst.org/). 

# 

# This program is free software: you can redistribute it and/or modify 

# it under the terms of the GNU General Public License as published by 

# the Free Software Foundation, either version 3 of the License, or 

# (at your option) any later version. 

# 

# This program is distributed in the hope that it will be useful, 

# but WITHOUT ANY WARRANTY; without even the implied warranty of 

# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

# GNU General Public License for more details. 

# 

# You should have received a copy of the LSST License Statement and 

# the GNU General Public License along with this program. If not, 

# see <https://www.lsstcorp.org/LegalNotices/>. 

# 

import sys 

import traceback 

import lsst.sphgeom 

 

import lsst.afw.image as afwImage 

import lsst.afw.geom as afwGeom 

import lsst.pex.config as pexConfig 

import lsst.pipe.base as pipeBase 

from lsst.skymap import DiscreteSkyMap, BaseSkyMap 

from lsst.pipe.base import ArgumentParser 

 

 

class MakeDiscreteSkyMapConfig(pexConfig.Config): 

"""Config for MakeDiscreteSkyMapTask 

""" 

coaddName = pexConfig.Field( 

doc="coadd name, e.g. deep, goodSeeing, chiSquared", 

dtype=str, 

default="deep", 

) 

skyMap = pexConfig.ConfigField( 

dtype=BaseSkyMap.ConfigClass, 

doc="SkyMap configuration parameters, excluding position and radius" 

) 

borderSize = pexConfig.Field( 

doc="additional border added to the bounding box of the calexps, in degrees", 

dtype=float, 

default=0.0 

) 

doAppend = pexConfig.Field( 

doc="append another tract to an existing DiscreteSkyMap on disk, if present?", 

dtype=bool, 

default=False 

) 

doWrite = pexConfig.Field( 

doc="persist the skyMap?", 

dtype=bool, 

default=True, 

) 

 

def setDefaults(self): 

self.skyMap.tractOverlap = 0.0 

 

 

class MakeDiscreteSkyMapRunner(pipeBase.TaskRunner): 

"""Run a task with all dataRefs at once, rather than one dataRef at a time. 

 

Call the run method of the task using two positional arguments: 

- butler: data butler 

- dataRefList: list of all dataRefs, 

""" 

@staticmethod 

def getTargetList(parsedCmd): 

return [(parsedCmd.butler, parsedCmd.id.refList)] 

 

def __call__(self, args): 

""" 

@param args Arguments for Task.run() 

 

@return: 

- None if self.doReturnResults false 

- A pipe_base Struct containing these fields if self.doReturnResults true: 

- dataRef: the provided data reference 

- metadata: task metadata after execution of run 

- result: result returned by task run, or None if the task fails 

""" 

butler, dataRefList = args 

task = self.TaskClass(config=self.config, log=self.log) 

result = None # in case the task fails 

exitStatus = 0 # exit status for shell 

92 ↛ 93line 92 didn't jump to line 93, because the condition on line 92 was never true if self.doRaise: 

result = task.run(butler, dataRefList) 

else: 

try: 

result = task.run(butler, dataRefList) 

except Exception as e: 

task.log.fatal("Failed: %s" % e) 

exitStatus = 1 

if not isinstance(e, pipeBase.TaskError): 

traceback.print_exc(file=sys.stderr) 

for dataRef in dataRefList: 

task.writeMetadata(dataRef) 

 

105 ↛ 113line 105 didn't jump to line 113, because the condition on line 105 was never false if self.doReturnResults: 

return pipeBase.Struct( 

dataRefList=dataRefList, 

metadata=task.metadata, 

result=result, 

exitStatus=exitStatus, 

) 

else: 

return pipeBase.Struct( 

exitStatus=exitStatus, 

) 

 

 

class MakeDiscreteSkyMapTask(pipeBase.CmdLineTask): 

"""!Make a DiscreteSkyMap in a repository, using the bounding box of a set of calexps. 

 

The command-line and run signatures and config are sufficiently different from MakeSkyMapTask 

that we don't inherit from it, but it is a replacement, so we use the same config/metadata names. 

""" 

ConfigClass = MakeDiscreteSkyMapConfig 

_DefaultName = "makeDiscreteSkyMap" 

RunnerClass = MakeDiscreteSkyMapRunner 

 

def __init__(self, **kwargs): 

pipeBase.CmdLineTask.__init__(self, **kwargs) 

 

@pipeBase.timeMethod 

def run(self, butler, dataRefList): 

"""!Make a skymap from the bounds of the given set of calexps. 

 

@param[in] butler data butler used to save the SkyMap 

@param[in] dataRefList dataRefs of calexps used to determine the size and pointing of the SkyMap 

@return a pipeBase Struct containing: 

- skyMap: the constructed SkyMap 

""" 

self.log.info("Extracting bounding boxes of %d images" % len(dataRefList)) 

points = [] 

for dataRef in dataRefList: 

143 ↛ 144line 143 didn't jump to line 144, because the condition on line 143 was never true if not dataRef.datasetExists("calexp"): 

self.log.warn("CalExp for %s does not exist: ignoring" % (dataRef.dataId,)) 

continue 

md = dataRef.get("calexp_md", immediate=True) 

wcs = afwGeom.makeSkyWcs(md) 

# nb: don't need to worry about xy0 because Exposure saves Wcs with CRPIX shifted by (-x0, -y0). 

boxI = afwImage.bboxFromMetadata(md) 

boxD = afwGeom.Box2D(boxI) 

points.extend(wcs.pixelToSky(corner).getVector() for corner in boxD.getCorners()) 

152 ↛ 153line 152 didn't jump to line 153, because the condition on line 152 was never true if len(points) == 0: 

raise RuntimeError("No data found from which to compute convex hull") 

self.log.info("Computing spherical convex hull") 

polygon = lsst.sphgeom.ConvexPolygon.convexHull(points) 

156 ↛ 157line 156 didn't jump to line 157, because the condition on line 156 was never true if polygon is None: 

raise RuntimeError( 

"Failed to compute convex hull of the vertices of all calexp bounding boxes; " 

"they may not be hemispherical." 

) 

circle = polygon.getBoundingCircle() 

 

datasetName = self.config.coaddName + "Coadd_skyMap" 

 

skyMapConfig = DiscreteSkyMap.ConfigClass() 

166 ↛ 167line 166 didn't jump to line 167, because the condition on line 166 was never true if self.config.doAppend and butler.datasetExists(datasetName): 

oldSkyMap = butler.get(datasetName, immediate=True) 

if not isinstance(oldSkyMap.config, DiscreteSkyMap.ConfigClass): 

raise TypeError("Cannot append to existing non-discrete skymap") 

compareLog = [] 

if not self.config.skyMap.compare(oldSkyMap.config, output=compareLog.append): 

raise ValueError("Cannot append to existing skymap - configurations differ:", *compareLog) 

skyMapConfig.raList.extend(oldSkyMap.config.raList) 

skyMapConfig.decList.extend(oldSkyMap.config.decList) 

skyMapConfig.radiusList.extend(oldSkyMap.config.radiusList) 

skyMapConfig.update(**self.config.skyMap.toDict()) 

circleCenter = lsst.sphgeom.LonLat(circle.getCenter()) 

skyMapConfig.raList.append(circleCenter[0].asDegrees()) 

skyMapConfig.decList.append(circleCenter[1].asDegrees()) 

circleRadiusDeg = circle.getOpeningAngle().asDegrees() 

skyMapConfig.radiusList.append(circleRadiusDeg + self.config.borderSize) 

skyMap = DiscreteSkyMap(skyMapConfig) 

 

for tractInfo in skyMap: 

wcs = tractInfo.getWcs() 

posBox = afwGeom.Box2D(tractInfo.getBBox()) 

pixelPosList = ( 

posBox.getMin(), 

afwGeom.Point2D(posBox.getMaxX(), posBox.getMinY()), 

posBox.getMax(), 

afwGeom.Point2D(posBox.getMinX(), posBox.getMaxY()), 

) 

skyPosList = [wcs.pixelToSky(pos).getPosition(afwGeom.degrees) for pos in pixelPosList] 

posStrList = ["(%0.3f, %0.3f)" % tuple(skyPos) for skyPos in skyPosList] 

self.log.info("tract %s has corners %s (RA, Dec deg) and %s x %s patches" % 

(tractInfo.getId(), ", ".join(posStrList), 

tractInfo.getNumPatches()[0], tractInfo.getNumPatches()[1])) 

198 ↛ 199line 198 didn't jump to line 199, because the condition on line 198 was never true if self.config.doWrite: 

butler.put(skyMap, datasetName) 

return pipeBase.Struct( 

skyMap=skyMap 

) 

 

def _getConfigName(self): 

"""Return None to disable saving config 

 

There's only one SkyMap per repository, so the config is redundant, and checking it means we can't 

easily overwrite or append to an existing repository. 

""" 

return None 

 

def _getMetadataName(self): 

"""Return None to disable saving metadata 

 

The metadata is not interesting, and by not saving it we can eliminate a dataset type. 

""" 

return None 

 

@classmethod 

def _makeArgumentParser(cls): 

parser = ArgumentParser(name=cls._DefaultName) 

parser.add_id_argument(name="--id", datasetType="calexp", help="data ID, e.g. --id visit=123 ccd=1,2") 

return parser