181 physicalFilters=None,
186 minOverlapFraction=None,
188 showPatchSelectedTractsOnly=False,
192 showRawOutlines=False,
194 trimToOverlappingTracts=False,
195 doUnscaledLimitRatio=False,
196 forceScaledLimitRatio=False,
197 maxVisitsForLegend=20,
198 useRubinPlotStyle=False,
202 imageDatasetType=None,
203 visitSummaryDatasetType=None,
205 if minOverlapFraction
is not None and tracts
is None:
206 raise RuntimeError(
"Must specify --tracts if --minOverlapFraction is set")
208 raise RuntimeError(
"--dpi must be > 0")
209 if maxVisitsForLegend < 0:
210 raise RuntimeError(
"--maxVisitsForLegend must be >= 0")
211 if maxVisits
is not None and maxVisits < 0:
212 raise RuntimeError(
"--maxVisits must be >= 0")
214 logger.info(
"Instantiating butler for repo '%s' with collections = %s", repo, collections)
215 butler = Butler.from_config(repo, collections=collections)
216 cameraDataset = butler.find_dataset(
"camera")
217 if cameraDataset
is None:
218 raise RuntimeError(
"Could not find required dataset type: camera")
219 instrument = str(cameraDataset.dataId[
"instrument"])
220 detectorSkipList = []
222 if skymapName
is None:
223 if instrument ==
"HSC":
224 skymapName =
"hsc_rings_v1"
225 detectorSkipList = [9]
226 elif instrument ==
"LSSTCam-imSim":
228 elif instrument ==
"LSSTComCamSim":
229 skymapName =
"ops_rehersal_prep_2k_v1"
230 elif instrument ==
"LATISS":
231 skymapName =
"latiss_v1"
232 elif instrument ==
"DECam":
233 skymapName =
"decam_rings_v1"
234 elif instrument ==
"LSSTComCam":
235 skymapName =
"lsst_cells_v1"
236 elif instrument ==
"LSSTCam":
237 skymapName =
"lsst_cells_v2"
240 f
"Unknown skymapName for instrument: {instrument}. Must specify --skymapName on command line."
243 logger.info(
"Using instrument = '%s' and skymapName = '%s'", instrument, skymapName)
244 camera = butler.get(
"camera", instrument=instrument)
245 skymap = butler.get(
"skyMap", instrument=instrument, skymap=skymapName)
248 if tracts
is not None:
252 if patches
is not None:
254 whereStr +=
" AND " + patchStr
if len(whereStr)
else patchStr
256 if visits
is not None:
258 if len(whereStr) < 1:
261 whereStr +=
" AND " + visitStr
263 if physicalFilters
is not None:
264 physicalFilterStr =
makeWhereInStr(
"physical_filter", physicalFilters, str)
265 whereStr +=
" AND " + physicalFilterStr
if len(whereStr)
else physicalFilterStr
267 if bands
is not None:
269 whereStr +=
" AND " + bandStr
if len(whereStr)
else bandStr
271 if radec
is not None:
273 radecStr = f
"visit.region OVERLAPS POINT({ra:.8f}, {dec:.8f})"
274 whereStr +=
" AND " + radecStr
if len(whereStr)
else radecStr
276 if len(whereStr) > 1:
277 whereStr = f
"instrument='{instrument}' AND skymap='{skymapName}' AND {whereStr}"
279 whereStr = f
"instrument='{instrument}' AND skymap='{skymapName}'"
280 logger.info(
"Querying the butler with the following dataId where clause: %s", whereStr)
283 butler, whereStr, imageDatasetType=imageDatasetType
285 logger.info(
"Using image dataset type: %s", imageDatasetTypeUsed)
287 failedDataIds = set()
289 processedDataIds = set((ref.dataId[
"visit"], ref.dataId[
"detector"])
for ref
in imageDataRefs)
290 selectedTracts = set(tracts)
if tracts
is not None else None
291 logger.info(
"Querying raw datasets to find failed detectors...")
292 rawDataRefs = list(butler.registry.queryDatasets(
"raw", where=whereStr).expanded())
293 logger.info(
"Found %d raw datasets", len(rawDataRefs))
294 for ref
in rawDataRefs:
295 exposure = ref.dataId[
"exposure"]
296 detector = ref.dataId[
"detector"]
297 if (exposure, detector)
not in processedDataIds:
298 if selectedTracts
is not None:
305 imageDatasetType=
"raw",
308 if raCorners
is None or decCorners
is None:
310 finiteCornerPairs = [
312 for ra, dec
in zip(raCorners, decCorners)
313 if np.isfinite(ra)
and np.isfinite(dec)
315 if len(finiteCornerPairs) == 0:
317 rawRas = [ra
for ra, _
in finiteCornerPairs]
318 rawDecs = [dec
for _, dec
in finiteCornerPairs]
319 rawTracts = set(skymap.findTractIdArray(rawRas, rawDecs, degrees=
True))
320 if selectedTracts.isdisjoint(rawTracts):
322 failedDataIds.add((exposure, detector))
324 "Found %d failed (raw but unprocessed) visit-detector pairs",
328 visitSummaryDatasetTypeUsed = visitSummaryDatasetType
331 if visitVetoFile
is not None:
332 with open(visitVetoFile)
as f:
333 content = f.readlines()
334 visitVetoList = [int(visit.strip())
for visit
in content]
339 for failVisit, _
in failedDataIds:
340 if failVisit
not in visits
and failVisit
not in visitVetoList:
341 visits.append(failVisit)
343 for dataRef
in imageDataRefs:
344 visit = dataRef.dataId.visit.id
345 if visit
not in visits
and visit
not in visitVetoList:
348 if maxVisits
is not None and len(visits) > maxVisits:
349 logger.info(
"Trimming visits from N=%d to N=%d due to --maxVisits", len(visits), maxVisits)
350 visits = visits[:maxVisits]
351 logger.info(
"List of visits (N=%d) satisfying selection filters: %s", len(visits), visits)
353 if len(visitVetoList) > 1:
354 visitListTemp = visits.copy()
355 for visit
in visitListTemp:
356 if visit
in visitVetoList:
358 logger.info(
"List of visits (N=%d) excluding veto list: %s}", len(visits), visits)
359 logger.info(
"List of visits (N=%d): %s", len(visits), visits)
364 butler, visits[0], visitSummaryDatasetType=visitSummaryDatasetType
366 logger.info(
"Using visit summary dataset type: %s", visitSummaryDatasetTypeUsed)
368 if visitSummaryDatasetType
is None:
370 "No visit summary dataset type found for auto-detection; "
371 "will fall back to detector-level WCS/bbox lookups."
375 "Configured visit summary dataset type '%s' was not found for sampled visit; "
376 "will fall back to detector-level WCS/bbox lookups.",
377 visitSummaryDatasetType,
384 (ccds
is None or ccdId
in ccds)
385 and ccd.getType() == DetectorType.SCIENCE
386 and ccdId
not in detectorSkipList
388 ccdIdList.append(ccdId)
390 nDetTot = len(ccdIdList)
391 missingVisitSummaryRows = {}
392 nonFiniteDetectorCorners = {}
394 visitIncludeList = []
398 if minOverlapFraction
is not None:
399 for i_v, visit
in enumerate(visits):
403 butler, visit, visitSummaryDatasetType=visitSummaryDatasetTypeUsed
405 except LookupError
as e:
406 logger.warning(
"%s Will try to get wcs from %s.", e, imageDatasetTypeUsed)
408 if tracts
is not None:
410 tractInfo = skymap[tract]
411 sphCorners = tractInfo.wcs.pixelToSky(
Box2D(tractInfo.bbox).getCorners())
412 tractConvexHull = ConvexPolygon.convexHull([coord.getVector()
for coord
in sphCorners])
413 for ccdId
in ccdIdList:
414 if ccdId
not in ccdOverlapList:
419 visitSummary=visitSummary,
421 imageDatasetType=imageDatasetTypeUsed,
424 if raCorners
is not None and decCorners
is not None:
425 finiteCornerPairs = [
427 for ra, dec
in zip(raCorners, decCorners)
428 if np.isfinite(ra)
and np.isfinite(dec)
430 if len(finiteCornerPairs) < 3:
432 "visit %d det %d (tract-overlap path): only %d finite "
433 "corner(s); raw ra=%s dec=%s",
436 len(finiteCornerPairs),
442 for ra, dec
in finiteCornerPairs:
444 detSphCorners.append(pt)
446 detConvexHull = ConvexPolygon.convexHull(
447 [coord.getVector()
for coord
in detSphCorners]
449 except ValueError
as e:
451 "visit %d det %d (tract-overlap path): hull ValueError (%s); "
452 "corners ra=%s dec=%s",
460 if tractConvexHull.contains(detConvexHull):
461 ccdOverlapList.append(ccdId)
463 if len(ccdOverlapList) / nDetTot >= minOverlapFraction:
465 if len(ccdOverlapList) / nDetTot < minOverlapFraction:
467 "Fraction of detectors overlapping any tract for visit %d (%.2f) < "
468 "minimum required (%.2f). Skipping visit...",
470 len(ccdOverlapList) / nDetTot,
474 if visit
not in visitIncludeList:
475 visitIncludeList.append(visit)
477 visitIncludeList = visits
481 ccdBBoxesPlotted = []
483 cmap =
get_cmap(len(visitIncludeList))
486 finalVisitColorIndices = []
488 includedPhysicalFilters = []
489 for i_v, visit
in enumerate(visitIncludeList):
490 print(
"Working on visit %d [%d of %d]" % (visit, i_v + 1, len(visitIncludeList)), end=
"\r")
492 defaultColor = cmap(i_v)
495 butler, visit, visitSummaryDatasetType=visitSummaryDatasetTypeUsed
497 except Exception
as e:
498 logger.warning(
"%s Will try to get wcs from %s.", e, imageDatasetTypeUsed)
501 band, physicalFilter =
getBand(visitSummary=visitSummary, butler=butler, visit=visit)
502 if band
not in includedBands:
503 includedBands.append(band)
504 if physicalFilter
not in includedPhysicalFilters:
505 includedPhysicalFilters.append(physicalFilter)
517 for ccdId
in ccdIdList:
518 if plotFailsOnly
and (visit, ccdId)
not in failedDataIds:
520 if showRawOutlines
and not plotFailsOnly:
527 imageDatasetType=
"raw",
530 if rawRaCorners
is not None and rawDecCorners
is not None:
531 rawCornerPairs = list(zip(rawRaCorners, rawDecCorners))
532 finiteRawCornerPairs = [
533 (ra, dec)
for ra, dec
in rawCornerPairs
if np.isfinite(ra)
and np.isfinite(dec)
535 if len(finiteRawCornerPairs) == len(rawCornerPairs):
549 visitSummary=
None if plotFailsOnly
else visitSummary,
551 imageDatasetType=
"raw" if plotFailsOnly
else imageDatasetTypeUsed,
552 missingVisitSummaryRows=missingVisitSummaryRows,
554 if raCorners
is not None and decCorners
is not None:
555 cornerPairs = list(zip(raCorners, decCorners))
556 finiteCornerPairs = [
557 (ra, dec)
for ra, dec
in cornerPairs
if np.isfinite(ra)
and np.isfinite(dec)
559 if len(finiteCornerPairs) < len(cornerPairs):
560 nonFiniteDetectorCorners.setdefault(visit, set()).add(ccdId)
566 if not inLegend
and len(visitIncludeList) <= maxVisitsForLegend:
567 plt.fill(raCorners, decCorners, label=str(visit), **fillKwargs)
570 plt.fill(raCorners, decCorners, **fillKwargs)
571 plt.fill(raCorners, decCorners, fill=
True, alpha=alphaEdge / 4, color=color, edgecolor=color)
572 if visit
not in finalVisitList:
573 finalVisitList.append(visit)
574 finalVisitColorIndices.append(i_v)
576 if showCcds
or showCcdsAll:
580 deltaRa = max(raCorners) - min(raCorners)
581 deltaDec = max(decCorners) - min(decCorners)
583 min(raCorners) + overlapFrac * deltaRa, min(decCorners) + overlapFrac * deltaDec
586 max(raCorners) - overlapFrac * deltaRa, max(decCorners) - overlapFrac * deltaDec
588 bboxDouble =
Box2D(minPoint, maxPoint)
589 ccdLabelsToPlot.append(
598 if visit
in missingVisitSummaryRows:
599 missingDetectors = sorted(missingVisitSummaryRows[visit])
601 "visit summary table for visit %d missing detectors: %s",
606 if visit
in nonFiniteDetectorCorners:
607 badDetectors = sorted(nonFiniteDetectorCorners[visit])
609 "Non-finite detector corners for visit %d (N=%d): %s",
616 "Final list of visits (N=%d) satisfying where and minOverlapFraction clauses: %s",
621 raToDecLimitRatio =
None
626 finiteCoordPairs = [(ra, dec)
for ra, dec
in zip(ras, decs)
if np.isfinite(ra)
and np.isfinite(dec)]
627 droppedCoordCount = len(ras) - len(finiteCoordPairs)
628 if droppedCoordCount > 0:
630 "Dropping %d non-finite detector corner coordinates before tract lookup",
633 if len(finiteCoordPairs) == 0:
634 if tracts
is not None:
636 "No finite detector corners found, but --tracts list was provided, so will go ahead and "
637 "plot the empty tracts."
642 raise RuntimeError(
"No finite detector corners available for tract lookup")
644 ras, decs = zip(*finiteCoordPairs)
647 tractList = list(set(skymap.findTractIdArray(ras, decs, degrees=
True)))
648 minVisitRa, maxVisitRa = min(ras), max(ras)
649 minVisitDec, maxVisitDec = min(decs), max(decs)
650 raVisitDiff = maxVisitRa - minVisitRa
651 decVisitDiff = maxVisitDec - minVisitDec
652 midVisitRa = minVisitRa + 0.5 * raVisitDiff
653 midVisitDec = minVisitDec + 0.5 * decVisitDiff
654 midRa = np.atleast_1d((midVisitRa * units.deg).to(units.radian).value).astype(np.float64)
655 midDec = np.atleast_1d((midVisitDec * units.deg).to(units.radian).value).astype(np.float64)
656 midSkyCoord = SkyCoord(midVisitRa * units.deg, midVisitDec * units.deg)
658 if tracts
is not None:
660 "No detectors were found, but --tracts list was provided, so will go ahead and "
661 "plot the empty tracts."
666 if radec
is not None:
668 "No detectors found; centering on provided RA/Dec (%.5f, %.5f).",
673 tractId = skymap.findTract(radecSphPoint).getId()
674 tractList = [tractId]
678 "No data to plot (if you want to plot empty tracts, include them as "
679 "a whitespace-separated list to the --tracts option)."
682 if len(invalidTracts) > 0:
683 logger.warning(
"Ignoring invalid tract ids: %s", invalidTracts)
684 if len(tractList) == 0:
685 raise RuntimeError(
"No valid tract ids found for plotting")
686 logger.info(
"List of tracts overlapping data: %s", tractList)
692 if trimToOverlappingTracts:
693 tractLimitsForPlotting = tractList
695 tractLimitsForPlotting = tracts
if tracts
is not None else tractList
697 tractLimitsForPlotting =
None
700 if tractLimitsForPlotting
is not None:
703 tractLimitsDict =
None
705 if forceScaledLimitRatio:
706 doUnscaledLimitRatio =
False
712 radiusMm = camera.computeMaxFocalPlaneRadius()
713 fpRadiusPt =
Point2D(radiusMm, radiusMm)
714 focalPlaneToFieldAngle = camera.getTransformMap().getTransform(FOCAL_PLANE, FIELD_ANGLE)
715 fpRadiusDeg = np.rad2deg(focalPlaneToFieldAngle.applyForward(fpRadiusPt))[0]
716 detectorRadiusDeg = fpRadiusDeg / np.sqrt(len(camera))
718 if tractLimitsDict
is not None:
720 xDelta0 = xLimMax - xLimMin
721 yDelta0 = yLimMax - yLimMin
723 xDelta0 = raVisitDiff
724 yDelta0 = decVisitDiff
725 yLimMin = minVisitDec
726 yLimMax = maxVisitDec
727 raDecScaleThresh = 1.5
729 (xDelta0 / yDelta0 > raDecScaleThresh
or yDelta0 / xDelta0 > raDecScaleThresh)
730 and max(xDelta0, yDelta0) > 70 * detectorRadiusDeg
735 "Sky coverage is large (and not too close to a pole), so not scaling to detector coords."
737 doUnscaledLimitRatio =
True
739 if not doUnscaledLimitRatio
and midSkyCoord
is not None:
742 minDistToMidCoord = 1e12
745 for i_v, visit
in enumerate(visits):
748 butler, visit, visitSummaryDatasetType=visitSummaryDatasetTypeUsed
750 except Exception
as e:
751 logger.warning(
"%s Will try to get wcs from %s.", e, imageDatasetTypeUsed)
753 for ccdId
in ccdIdList:
758 visitSummary=visitSummary,
760 imageDatasetType=imageDatasetTypeUsed,
763 if raCorners
is not None and decCorners
is not None:
764 finiteCornerPairs = [
766 for ra, dec
in zip(raCorners, decCorners)
767 if np.isfinite(ra)
and np.isfinite(dec)
769 if len(finiteCornerPairs) < 3:
771 "visit %d det %d (midpoint path): only %d finite corner(s); raw ra=%s dec=%s",
774 len(finiteCornerPairs),
780 for ra, dec
in finiteCornerPairs:
782 detSphCorners.append(pt)
783 ptSkyCoord = SkyCoord(ra * units.deg, dec * units.deg)
784 separation = (midSkyCoord.separation(ptSkyCoord)).degree
785 if separation < minDistToMidCoord:
788 minDistToMidCoord = separation
790 detConvexHull =
ConvexPolygon([coord.getVector()
for coord
in detSphCorners])
791 except ValueError
as e:
793 "visit %d det %d (midpoint path): hull ValueError (%s); corners ra=%s dec=%s",
801 if detConvexHull.contains(midRa, midDec)
and raToDecLimitRatio
is None:
803 "visit/det overlapping plot coord mid point in RA/Dec: %d %d", visit, ccdId
805 raToDecLimitRatio = (max(raCorners) - min(raCorners)) / (
806 max(decCorners) - min(decCorners)
809 width = det.getBBox().getWidth()
810 height = det.getBBox().getHeight()
811 if raToDecLimitRatio > 1.0:
812 raToDecLimitRatio /= max(height / width, width / height)
814 if raToDecLimitRatio < 1.0:
815 raToDecLimitRatio *= max(height / width, width / height)
817 if raToDecLimitRatio
is not None:
820 if raToDecLimitRatio
is None and minSepVisit
is not None:
821 canComputeNearestDetRatio =
True
824 butler, minSepVisit, visitSummaryDatasetType=visitSummaryDatasetTypeUsed
826 except Exception
as e:
827 logger.warning(
"%s Will try to get wcs from %s.", e, imageDatasetTypeUsed)
833 visitSummary=visitSummary,
835 imageDatasetType=imageDatasetTypeUsed,
838 if raCorners
is None or decCorners
is None:
840 "Could not determine detector corners for visit/det nearest plot midpoint: %d %d",
844 canComputeNearestDetRatio =
False
846 finiteCornerPairs = [
848 for ra, dec
in zip(raCorners, decCorners)
849 if np.isfinite(ra)
and np.isfinite(dec)
851 if len(finiteCornerPairs) < 3:
853 "Insufficient finite detector corners for visit/det nearest plot midpoint: %d %d",
857 canComputeNearestDetRatio =
False
859 raCorners = [ra
for ra, _
in finiteCornerPairs]
860 decCorners = [dec
for _, dec
in finiteCornerPairs]
861 if canComputeNearestDetRatio:
863 "visit/det closest to plot coord mid point in RA/Dec (none actually overlap it): %d %d",
867 raToDecLimitRatio = (max(raCorners) - min(raCorners)) / (max(decCorners) - min(decCorners))
868 det = camera[minSepCcdId]
869 width = det.getBBox().getWidth()
870 height = det.getBBox().getHeight()
871 if raToDecLimitRatio > 1.0:
872 raToDecLimitRatio /= max(height / width, width / height)
874 if raToDecLimitRatio < 1.0:
875 raToDecLimitRatio *= max(height / width, width / height)
877 elif not doUnscaledLimitRatio:
879 "Skipping midpoint-based detector aspect-ratio scaling: "
880 "no detector-derived midpoint is available."
883 if plotFailsOnly
and not doUnscaledLimitRatio
and raToDecLimitRatio
is None:
886 raToDecLimitRatio = 1.0
888 if tractLimitsDict
is not None:
889 xlim, ylim =
derivePlotLimits(tractLimitsDict, raToDecLimitRatio=raToDecLimitRatio, buffFrac=0.04)
891 visitLimitsDict = {
"allVisits": {
"ras": [minVisitRa, maxVisitRa],
"decs": [minVisitDec, maxVisitDec]}}
892 xlim, ylim =
derivePlotLimits(visitLimitsDict, raToDecLimitRatio=raToDecLimitRatio, buffFrac=0.04)
894 if doUnscaledLimitRatio:
895 boxAspectRatio = abs((ylim[1] - ylim[0]) / (xlim[1] - xlim[0]))
900 ccdLabelEdgeBuffer = 0.03
901 ccdBufRa = ccdLabelEdgeBuffer * abs(xlim[1] - xlim[0])
902 ccdBufDec = ccdLabelEdgeBuffer * abs(ylim[1] - ylim[0])
903 for ccdLabel
in ccdLabelsToPlot:
906 min(xlim) + ccdBufRa < ccdLabel[
"ra"] < max(xlim) - ccdBufRa
907 and min(ylim) + ccdBufDec < ccdLabel[
"dec"] < max(ylim) - ccdBufDec
913 str(ccdLabel[
"ccdId"]),
920 overlaps = [ccdLabel[
"bbox"].overlaps(otherBbox)
for otherBbox
in ccdBBoxesPlotted]
921 if not any(overlaps):
925 str(ccdLabel[
"ccdId"]),
931 ccdBBoxesPlotted.append(ccdLabel[
"bbox"])
937 if tracts
is not None:
938 tractOutlineList = list(set(tracts + tractList))
940 tractOutlineList = tractList
941 tractOutlineList.sort()
942 selectedPatchTracts = set(tracts)
if tracts
is not None else None
943 if showPatchSelectedTractsOnly
and selectedPatchTracts
is None:
945 "--showPatchSelectedTractsOnly was set without --tracts; showing patches for all plotted tracts."
947 logger.info(
"List of tract outlines being plotted: %s", tractOutlineList)
948 for i_t, tract
in enumerate(tractOutlineList):
949 alpha = max(0.1, alpha0 - i_t * 1.0 / len(tractOutlineList))
950 tractInfo = skymap[tract]
951 tCenter = tractInfo.ctr_coord
952 tCenterRa = tCenter.getRa().asDegrees()
953 tCenterDec = tCenter.getDec().asDegrees()
954 fracDeltaX = 0.02 * abs((xlim[1] - xlim[0]))
955 fracDeltaY = 0.02 * abs((ylim[1] - ylim[0]))
957 xlim[1] + fracDeltaX < tCenterRa < xlim[0] - fracDeltaX
958 and ylim[0] + fracDeltaY < tCenterDec < ylim[1] - fracDeltaY
960 if len(tractOutlineList) > 1
or not showPatch:
962 plt.text(tCenterRa, tCenterDec, tract, fontsize=7, alpha=alpha, ha=
"center", va=
"center")
971 path_effects=[pathEffects.withStroke(linewidth=3, foreground=
"black")],
977 ra, dec =
bboxToRaDec(tractInfo.bbox, tractInfo.getWcs())
978 plt.fill(ra, dec, fill=
False, edgecolor=
"k", lw=1, linestyle=
"dashed", alpha=alpha)
979 tractArtist = matplotlib.patches.Patch(fill=
False, edgecolor=
"k", linestyle=
"dashed", alpha=alpha)
980 tractHandleList.append(tractArtist)
981 tractStrList.append(str(tract))
983 not showPatchSelectedTractsOnly
or selectedPatchTracts
is None or tract
in selectedPatchTracts
986 for patch
in tractInfo:
987 ra, dec =
bboxToRaDec(patch.getInnerBBox(), tractInfo.getWcs())
988 plt.fill(ra, dec, fill=
False, edgecolor=patchColor, lw=0.5, linestyle=
"dotted", alpha=alpha)
996 str(patch.sequential_index),
1005 if radec
is not None:
1012 markeredgecolor=
"black",
1013 markeredgewidth=0.8,
1021 ax.set_box_aspect(boxAspectRatio)
1022 if abs(xlim[1] > 99.99):
1023 ax.tick_params(
"x", labelrotation=45, pad=0, labelsize=8)
1025 ax.tick_params(
"x", labelrotation=0, pad=0, labelsize=8)
1026 ax.tick_params(
"y", labelsize=8)
1027 ax.set_xlabel(
"RA (deg)", fontsize=9)
1028 ax.set_ylabel(
"Dec (deg)", fontsize=9)
1030 if len(visitIncludeList) > maxVisitsForLegend:
1031 nVisits = len(finalVisitList)
1032 nz = matplotlib.colors.Normalize(vmin=0, vmax=len(visitIncludeList) - 1)
1033 cax, _ = matplotlib.colorbar.make_axes(plt.gca(), pad=0.03)
1034 cax.tick_params(labelsize=7)
1035 cb = matplotlib.colorbar.ColorbarBase(cax, cmap=cmap, norm=nz, alpha=alphaEdge)
1036 nTicks = min(8, nVisits)
1037 tickPositions = [finalVisitColorIndices[int(round(x))]
for x
in np.linspace(0, nVisits - 1, nTicks)]
1038 cb.set_ticks(tickPositions)
1039 cb.set_ticklabels([str(finalVisitList[int(round(x))])
for x
in np.linspace(0, nVisits - 1, nTicks)])
1040 cb.ax.yaxis.get_offset_text().set_fontsize(7)
1041 cb.set_label(
"visit", rotation=-90, labelpad=13, fontsize=9)
1042 tractLegend = Legend(
1053 ax.add_artist(tractLegend)
1055 if len(visitIncludeList) > 0:
1056 handles, labels = ax.get_legend_handles_labels()
1057 if len(handles) > 0:
1058 xBboxAnchor = min(1.25, max(1.03, boxAspectRatio * 1.15))
1061 bbox_to_anchor=(xBboxAnchor, 0.5),
1069 tractLegend = Legend(
1074 bbox_to_anchor=(1.0, 0.5),
1081 ax.add_artist(tractLegend)
1083 titleStr = repo +
"\n" + collections[0]
1084 if len(collections) > 1:
1085 for collection
in collections[1:]:
1086 titleStr +=
"\n" + collection
1088 titleStr +=
"\nFailed calibration: N={}".
format(len(failedDataIds))
1090 titleStr +=
"\nnVisit: {}".
format(str(len(finalVisitList)))
1091 if minOverlapFraction
is not None:
1092 titleStr +=
" (minOverlapFraction = {:.2f})".
format(minOverlapFraction)
1093 if len(includedBands) > 0:
1094 titleStr +=
", bands: {}".
format(str(includedBands).translate({ord(i):
None for i
in "[]'"}))
1095 if len(includedPhysicalFilters) > 0:
1096 if len(includedPhysicalFilters[0]) > 9:
1100 titleStr +=
" physical filters: {}".
format(
1101 str(includedPhysicalFilters).translate({ord(i):
None for i
in "[]'"})
1103 ax.set_title(
"{}".
format(titleStr), fontsize=8)
1106 if boxAspectRatio > 1.0:
1107 minInches = max(4.0, 0.3 * abs(xlim[1] - xlim[0]))
1109 yInches = min(120.0, boxAspectRatio * minInches)
1110 fig.set_size_inches(xInches, yInches)
1111 if boxAspectRatio < 1.0:
1112 minInches = max(4.0, 0.3 * abs(ylim[1] - ylim[0]))
1113 xInches = min(120.0, minInches / boxAspectRatio)
1115 fig.set_size_inches(xInches, yInches)
1116 if saveFile
is not None:
1117 fileRoot, fileExt = os.path.splitext(saveFile)
1118 if plotFailsOnly
and "fail" not in saveFile:
1119 saveFile = f
"{fileRoot}_failed{fileExt}"
1121 defaultSaveFormat = matplotlib.rcParams.get(
"savefig.format",
"png")
1122 saveFile = f
"{saveFile}.{defaultSaveFormat}"
1123 saveFile = os.path.normpath(saveFile)
1124 logger.info(
"Saving file in: %s", saveFile)
1125 fig.savefig(saveFile, bbox_inches=
"tight", dpi=dpi)