|
22 | 22 | "Data Release: DP1 <br>\n", |
23 | 23 | "Container Size: large <br>\n", |
24 | 24 | "LSST Science Pipelines version: r29.2.0 <br>\n", |
25 | | - "Last verified to run: 2025-11-20 <br>\n", |
| 25 | + "Last verified to run: 2025-12-16 <br>\n", |
26 | 26 | "Repository: <a href=\"https://github.com/lsst/tutorial-notebooks\">github.com/lsst/tutorial-notebooks</a> <br>" |
27 | 27 | ] |
28 | 28 | }, |
|
98 | 98 | "source": [ |
99 | 99 | "import numpy as np\n", |
100 | 100 | "import matplotlib.pyplot as plt\n", |
101 | | - "import os\n", |
102 | 101 | "\n", |
103 | 102 | "import lsst.afw.display as afwDisplay\n", |
| 103 | + "import lsst.afw.image as afwImage\n", |
104 | 104 | "from lsst.afw.image import ExposureF\n", |
105 | 105 | "from lsst.afw.fits import MemFileManager\n", |
106 | 106 | "\n", |
| 107 | + "\n", |
107 | 108 | "from lsst.rsp.utils import get_pyvo_auth\n", |
108 | 109 | "from lsst.rsp.service import get_siav2_service\n", |
109 | 110 | "\n", |
|
113 | 114 | "\n", |
114 | 115 | "from astropy import units as u\n", |
115 | 116 | "from astropy.coordinates import Angle\n", |
116 | | - "from astropy.time import Time" |
| 117 | + "from astropy.time import Time\n", |
| 118 | + "\n", |
| 119 | + "from astropy.io import fits\n", |
| 120 | + "import io\n" |
117 | 121 | ] |
118 | 122 | }, |
119 | 123 | { |
|
324 | 328 | "id": "d945e191-7ee4-458a-bb0f-fb00cd219e30", |
325 | 329 | "metadata": {}, |
326 | 330 | "source": [ |
327 | | - "Lastly, call the Rubin Image Cutout Service. This is the IVOA procedure `cutout-sync` that is called using `get_adhocservice_by_id`. It is done by feeding the data link created above (called `dl_result`) to `from_resource`. Since the Rubin DP1 imaging is proprietary it is necessary to again provide the authorization for the current RSP session. Do this using the `get_pyvo_auth` function." |
| 331 | + "Lastly, call the Rubin Image Cutout Service. This notebook demonstrates two types of cutout services: `cutout-sync` which, by default, returns just the image extension of the LSST imaging; `cutout-sync-exposure`, which returns the full set of metadata and image extensions that are contained in the `ExposureF` data type. This section will demonstrate the use of `cutout-sync-exposure` and Section 4 below will demonstrate the other. \n", |
| 332 | + "\n", |
| 333 | + "To use the cutout service in this example, the IVOA procedure `cutout-sync-exposure` is called using `get_adhocservice_by_id`. It is done by feeding the data link created above (called `dl_result`) to `from_resource`. Since the Rubin DP1 imaging is proprietary it is necessary to again provide the authorization for the current RSP session. Do this using the `get_pyvo_auth` function." |
328 | 334 | ] |
329 | 335 | }, |
330 | 336 | { |
|
335 | 341 | "outputs": [], |
336 | 342 | "source": [ |
337 | 343 | "sq = SodaQuery.from_resource(dl_result,\n", |
338 | | - " dl_result.get_adhocservice_by_id(\"cutout-sync\"),\n", |
| 344 | + " dl_result.get_adhocservice_by_id(\"cutout-sync-exposure\"),\n", |
339 | 345 | " session=get_pyvo_auth())" |
340 | 346 | ] |
341 | 347 | }, |
|
386 | 392 | "sq.raise_if_error()\n", |
387 | 393 | "mem = MemFileManager(len(cutout_bytes))\n", |
388 | 394 | "mem.setData(cutout_bytes, len(cutout_bytes))\n", |
389 | | - "exposure = ExposureF(mem)" |
| 395 | + "exposure = ExposureF(mem)\n" |
390 | 396 | ] |
391 | 397 | }, |
392 | 398 | { |
393 | 399 | "cell_type": "markdown", |
394 | 400 | "id": "324be87a-337b-4db7-b57d-842f14614613", |
395 | 401 | "metadata": {}, |
396 | 402 | "source": [ |
397 | | - "Display the cutout." |
| 403 | + "Display the image extension of the `ExposureF` cutout." |
398 | 404 | ] |
399 | 405 | }, |
400 | 406 | { |
|
468 | 474 | "metadata": {}, |
469 | 475 | "outputs": [], |
470 | 476 | "source": [ |
471 | | - "# from astropy.io import fits\n", |
472 | | - "\n", |
473 | 477 | "# fits.info(sodaCutout)\n", |
474 | 478 | "\n", |
475 | 479 | "# sodaCutout_small = os.path.join(tempdir, 'cutout-circle_small.fits')\n", |
|
502 | 506 | "outputs": [], |
503 | 507 | "source": [ |
504 | 508 | "sqp = SodaQuery.from_resource(dl_result,\n", |
505 | | - " dl_result.get_adhocservice_by_id(\"cutout-sync\"),\n", |
| 509 | + " dl_result.get_adhocservice_by_id(\"cutout-sync-exposure\"),\n", |
506 | 510 | " session=get_pyvo_auth())\n", |
507 | 511 | "\n", |
508 | 512 | "ra_edge = 0.02 * u.deg\n", |
|
565 | 569 | "outputs": [], |
566 | 570 | "source": [ |
567 | 571 | "sq2 = SodaQuery.from_resource(dl_result,\n", |
568 | | - " dl_result.get_adhocservice_by_id(\"cutout-sync\"),\n", |
| 572 | + " dl_result.get_adhocservice_by_id(\"cutout-sync-exposure\"),\n", |
569 | 573 | " session=get_pyvo_auth())\n", |
570 | 574 | "\n", |
571 | 575 | "edge1 = Radius\n", |
|
604 | 608 | "cosd = np.cos(a.radian)\n", |
605 | 609 | "\n", |
606 | 610 | "sq3 = SodaQuery.from_resource(dl_result,\n", |
607 | | - " dl_result.get_adhocservice_by_id(\"cutout-sync\"),\n", |
| 611 | + " dl_result.get_adhocservice_by_id(\"cutout-sync-exposure\"),\n", |
608 | 612 | " session=get_pyvo_auth())\n", |
609 | 613 | "\n", |
610 | 614 | "sq3.polygon = (spherePoint.getRa().asDegrees() * u.deg - edge1/cosd,\n", |
|
674 | 678 | "The zooniverse package should be used instead of this procedure for citizen science applications. " |
675 | 679 | ] |
676 | 680 | }, |
| 681 | + { |
| 682 | + "cell_type": "markdown", |
| 683 | + "id": "99e75a7a-348e-409e-88f6-871b68a4e9dc", |
| 684 | + "metadata": {}, |
| 685 | + "source": [ |
| 686 | + "## 4. Single extension image cutouts\n", |
| 687 | + "\n", |
| 688 | + "As mentioned in Section 3, there are three types of cutout services: `cutout-sync-exposure` to return an `ExposureF` type image with all LSST image extensions (demonstrated above); `cutout-sync` which returns just the image extension. The latter will be demonstrated below.\n", |
| 689 | + "\n", |
| 690 | + "First, reuse the data link `dl_result` that was defined in Section 3 to define a new cutout, this time using `cutout-sync`. Call this soda query `sq4` to differentiate from the earlier ones. Define the cutout as a square subtended by a circle of radius 0.01 degrees, as done in Section 3.\n" |
| 691 | + ] |
| 692 | + }, |
| 693 | + { |
| 694 | + "cell_type": "code", |
| 695 | + "execution_count": null, |
| 696 | + "id": "837fbd2a-c3e2-4ee3-a06d-69f4df50823b", |
| 697 | + "metadata": {}, |
| 698 | + "outputs": [], |
| 699 | + "source": [ |
| 700 | + "sq4 = SodaQuery.from_resource(dl_result,\n", |
| 701 | + " dl_result.get_adhocservice_by_id(\"cutout-sync\"),\n", |
| 702 | + " session=get_pyvo_auth())\n", |
| 703 | + "\n", |
| 704 | + "spherePoint = geom.SpherePoint(target_ra*geom.degrees, target_dec*geom.degrees)\n", |
| 705 | + "Radius = 0.01 * u.deg\n", |
| 706 | + "\n", |
| 707 | + "sq4.circle = (spherePoint.getRa().asDegrees() * u.deg,\n", |
| 708 | + " spherePoint.getDec().asDegrees() * u.deg,\n", |
| 709 | + " Radius)\n", |
| 710 | + "\n", |
| 711 | + "cutout_bytes = sq4.execute_stream().read()\n", |
| 712 | + "sq4.raise_if_error()\n" |
| 713 | + ] |
| 714 | + }, |
| 715 | + { |
| 716 | + "cell_type": "markdown", |
| 717 | + "id": "454a017b-df88-48cc-975e-cac4ff3a37b5", |
| 718 | + "metadata": {}, |
| 719 | + "source": [ |
| 720 | + "Receive the cutout into memory, as in Section 3." |
| 721 | + ] |
| 722 | + }, |
| 723 | + { |
| 724 | + "cell_type": "code", |
| 725 | + "execution_count": null, |
| 726 | + "id": "516dbd86-d573-4d81-a017-2ebbce8fb445", |
| 727 | + "metadata": {}, |
| 728 | + "outputs": [], |
| 729 | + "source": [ |
| 730 | + "mem = MemFileManager(len(cutout_bytes))\n", |
| 731 | + "mem.setData(cutout_bytes, len(cutout_bytes))\n" |
| 732 | + ] |
| 733 | + }, |
| 734 | + { |
| 735 | + "cell_type": "markdown", |
| 736 | + "id": "2ae72a88-8002-46b5-8eee-164a2c903314", |
| 737 | + "metadata": {}, |
| 738 | + "source": [ |
| 739 | + "Use the `getData` method from the `MemFileManager` class to return and store the bytes associated with the cutout. " |
| 740 | + ] |
| 741 | + }, |
| 742 | + { |
| 743 | + "cell_type": "code", |
| 744 | + "execution_count": null, |
| 745 | + "id": "ed3f7908-a256-42f5-939f-61f38645c7ca", |
| 746 | + "metadata": {}, |
| 747 | + "outputs": [], |
| 748 | + "source": [ |
| 749 | + "data = mem.getData()" |
| 750 | + ] |
| 751 | + }, |
| 752 | + { |
| 753 | + "cell_type": "markdown", |
| 754 | + "id": "441a49fb-42be-4f37-853e-141419f1da14", |
| 755 | + "metadata": {}, |
| 756 | + "source": [ |
| 757 | + "Use the astropy.io fits package to access the bytes and store in an HDUList object. Then, the image extension `image_hdu`, and pixel values `image_data` (a numpy array) can be accessed similar to opening a fits file. " |
| 758 | + ] |
| 759 | + }, |
| 760 | + { |
| 761 | + "cell_type": "code", |
| 762 | + "execution_count": null, |
| 763 | + "id": "d23a0397-6500-4e36-b956-7917ed6a7173", |
| 764 | + "metadata": {}, |
| 765 | + "outputs": [], |
| 766 | + "source": [ |
| 767 | + "hdul = fits.open(io.BytesIO(data))\n", |
| 768 | + "hdul.info()\n", |
| 769 | + "\n", |
| 770 | + "image_hdu = hdul[1]\n", |
| 771 | + "image_data = image_hdu.data\n" |
| 772 | + ] |
| 773 | + }, |
| 774 | + { |
| 775 | + "cell_type": "markdown", |
| 776 | + "id": "c0eea8e5-ae86-456d-9d67-798557a4a707", |
| 777 | + "metadata": {}, |
| 778 | + "source": [ |
| 779 | + "To enable image display, first store `image_data` as an LSST `ImageF` object." |
| 780 | + ] |
| 781 | + }, |
| 782 | + { |
| 783 | + "cell_type": "code", |
| 784 | + "execution_count": null, |
| 785 | + "id": "78e1c658-916c-4a73-8c0b-a50d281f4764", |
| 786 | + "metadata": {}, |
| 787 | + "outputs": [], |
| 788 | + "source": [ |
| 789 | + "image = afwImage.ImageF(image_data.astype(\"float32\"))\n" |
| 790 | + ] |
| 791 | + }, |
| 792 | + { |
| 793 | + "cell_type": "markdown", |
| 794 | + "id": "6a19e7cd-4096-44b9-832f-baa6b6c40482", |
| 795 | + "metadata": {}, |
| 796 | + "source": [ |
| 797 | + "Display the image cutout." |
| 798 | + ] |
| 799 | + }, |
| 800 | + { |
| 801 | + "cell_type": "code", |
| 802 | + "execution_count": null, |
| 803 | + "id": "1aa9009a-b493-41ea-bc3c-e0c79adef913", |
| 804 | + "metadata": {}, |
| 805 | + "outputs": [], |
| 806 | + "source": [ |
| 807 | + "display = afwDisplay.Display()\n", |
| 808 | + "display.scale('asinh', 'zscale')\n", |
| 809 | + "display.image(image)\n", |
| 810 | + "plt.show()" |
| 811 | + ] |
| 812 | + }, |
677 | 813 | { |
678 | 814 | "attachments": { |
679 | 815 | "923e951d-30c0-4a8f-a1b0-8bb851f1cb5f.png": { |
|
684 | 820 | "id": "acbe9638-e7b7-4229-8301-3f57eb0b0c5b", |
685 | 821 | "metadata": {}, |
686 | 822 | "source": [ |
687 | | - "## 4. Exercise for the learner\n", |
| 823 | + "## 5. Exercise for the learner\n", |
688 | 824 | "\n", |
689 | 825 | "Reproduce the cutout below, whose center is (ra, dec) = 59.1, -48.8 with 0.06 degrees on a side.\n", |
690 | 826 | "\n", |
|
694 | 830 | "\n", |
695 | 831 | "</div>\n" |
696 | 832 | ] |
697 | | - }, |
698 | | - { |
699 | | - "cell_type": "code", |
700 | | - "execution_count": null, |
701 | | - "id": "6b611f9f-aaa5-4e2b-b4a5-b7acc6aecbf9", |
702 | | - "metadata": {}, |
703 | | - "outputs": [], |
704 | | - "source": [] |
705 | 833 | } |
706 | 834 | ], |
707 | 835 | "metadata": { |
|
0 commit comments