|
18 | 18 |
|
19 | 19 | from astroquery.query import BaseVOQuery
|
20 | 20 | from astroquery.utils import commons
|
21 |
| -from astroquery.exceptions import LargeQueryWarning, NoResultsWarning |
| 21 | +from astroquery.exceptions import NoResultsWarning |
22 | 22 | from astroquery.simbad.utils import (_catch_deprecated_fields_with_arguments,
|
23 | 23 | _wildcard_to_regexp, CriteriaTranslator,
|
24 | 24 | query_criteria_fields)
|
@@ -108,6 +108,7 @@ def __init__(self, ROW_LIMIT=None):
|
108 | 108 | self._server = conf.server
|
109 | 109 | self._tap = None
|
110 | 110 | self._hardlimit = None
|
| 111 | + self._uploadlimit = None |
111 | 112 | # attributes to construct ADQL queries
|
112 | 113 | self._columns_in_output = None # a list of _Column
|
113 | 114 | self.joins = [] # a list of _Join
|
@@ -166,6 +167,12 @@ def hardlimit(self):
|
166 | 167 | self._hardlimit = self.tap.hardlimit
|
167 | 168 | return self._hardlimit
|
168 | 169 |
|
| 170 | + @property |
| 171 | + def uploadlimit(self): |
| 172 | + if self._uploadlimit is None: |
| 173 | + self._uploadlimit = self.tap.get_tap_capability().uploadlimit.hard.content |
| 174 | + return self._uploadlimit |
| 175 | + |
169 | 176 | @property
|
170 | 177 | def columns_in_output(self):
|
171 | 178 | """A list of _Column.
|
@@ -698,7 +705,6 @@ def query_objects(self, object_names, *, wildcard=False, criteria=None,
|
698 | 705 |
|
699 | 706 | @deprecated_renamed_argument(["equinox", "epoch", "cache"],
|
700 | 707 | new_name=[None]*3,
|
701 |
| - alternative=["astropy.coordinates.SkyCoord"]*3, |
702 | 708 | since=['0.4.8']*3, relax=True)
|
703 | 709 | def query_region(self, coordinates, radius=2*u.arcmin, *,
|
704 | 710 | criteria=None, get_query_payload=False,
|
@@ -768,42 +774,54 @@ def query_region(self, coordinates, radius=2*u.arcmin, *,
|
768 | 774 | center = center.transform_to("icrs")
|
769 | 775 |
|
770 | 776 | top, columns, joins, instance_criteria = self._get_query_parameters()
|
| 777 | + if criteria: |
| 778 | + instance_criteria.append(f"({criteria})") |
771 | 779 |
|
772 | 780 | if center.isscalar:
|
773 | 781 | radius = coord.Angle(radius)
|
774 | 782 | instance_criteria.append(f"CONTAINS(POINT('ICRS', basic.ra, basic.dec), "
|
775 | 783 | f"CIRCLE('ICRS', {center.ra.deg}, {center.dec.deg}, "
|
776 | 784 | f"{radius.to(u.deg).value})) = 1")
|
| 785 | + return self._query(top, columns, joins, instance_criteria, |
| 786 | + get_query_payload=get_query_payload) |
777 | 787 |
|
| 788 | + # from uploadLimit in SIMBAD's capabilities |
| 789 | + # http://simbad.cds.unistra.fr/simbad/sim-tap/capabilities |
| 790 | + if len(center) > self.uploadlimit: |
| 791 | + raise ValueError(f"'query_region' can process up to {self.uploadlimit} " |
| 792 | + "centers. For larger queries, split your centers list.") |
| 793 | + |
| 794 | + # `radius` as `str` is iterable, but contains only one value. |
| 795 | + if isiterable(radius) and not isinstance(radius, str): |
| 796 | + if len(radius) != len(center): |
| 797 | + raise ValueError(f"Mismatch between radii of length {len(radius)}" |
| 798 | + f" and center coordinates of length {len(center)}.") |
| 799 | + radius = [coord.Angle(item).to(u.deg).value for item in radius] |
778 | 800 | else:
|
779 |
| - if len(center) > 10000: |
780 |
| - warnings.warn( |
781 |
| - "For very large queries, you may receive a timeout error. SIMBAD suggests" |
782 |
| - " splitting queries with >10000 entries into multiple threads", |
783 |
| - LargeQueryWarning, stacklevel=2 |
784 |
| - ) |
785 |
| - |
786 |
| - # `radius` as `str` is iterable, but contains only one value. |
787 |
| - if isiterable(radius) and not isinstance(radius, str): |
788 |
| - if len(radius) != len(center): |
789 |
| - raise ValueError(f"Mismatch between radii of length {len(radius)}" |
790 |
| - f" and center coordinates of length {len(center)}.") |
791 |
| - radius = [coord.Angle(item) for item in radius] |
792 |
| - else: |
793 |
| - radius = [coord.Angle(radius)] * len(center) |
| 801 | + radius = [coord.Angle(radius).to(u.deg).value] * len(center) |
794 | 802 |
|
| 803 | + # for small number of centers, not using the upload method is faster |
| 804 | + if len(center) <= 300: |
795 | 805 | cone_criteria = [(f"CONTAINS(POINT('ICRS', basic.ra, basic.dec), CIRCLE('ICRS', "
|
796 |
| - f"{center.ra.deg}, {center.dec.deg}, {radius.to(u.deg).value})) = 1 ") |
| 806 | + f"{center.ra.deg}, {center.dec.deg}, {radius})) = 1 ") |
797 | 807 | for center, radius in zip(center, radius)]
|
798 | 808 |
|
799 | 809 | cone_criteria = f" ({'OR '.join(cone_criteria)})"
|
800 | 810 | instance_criteria.append(cone_criteria)
|
801 | 811 |
|
802 |
| - if criteria: |
803 |
| - instance_criteria.append(f"({criteria})") |
| 812 | + return self._query(top, columns, joins, instance_criteria, |
| 813 | + get_query_payload=get_query_payload) |
| 814 | + |
| 815 | + # for longer centers list, we use a TAP upload |
| 816 | + upload_centers = Table({"ra": center.ra.deg, "dec": center.dec.deg, |
| 817 | + "radius": radius}) |
| 818 | + sub_query = "(SELECT ra, dec, radius FROM TAP_UPLOAD.centers) AS centers" |
| 819 | + instance_criteria.append("CONTAINS(POINT('ICRS', basic.ra, basic.dec), CIRCLE" |
| 820 | + "('ICRS', centers.ra, centers.dec, centers.radius)) = 1 ") |
804 | 821 |
|
805 | 822 | return self._query(top, columns, joins, instance_criteria,
|
806 |
| - get_query_payload=get_query_payload) |
| 823 | + from_table=f"{sub_query}, basic", |
| 824 | + get_query_payload=get_query_payload, centers=upload_centers) |
807 | 825 |
|
808 | 826 | @deprecated_renamed_argument(["verbose", "cache"], new_name=[None, None],
|
809 | 827 | since=['0.4.8', '0.4.8'], relax=True)
|
|
0 commit comments