|
| 1 | +import asyncio |
| 2 | +from contextlib import asynccontextmanager |
| 3 | + |
| 4 | +import click |
| 5 | + |
| 6 | +from planet.cli.cmds import command |
| 7 | +from planet.cli.io import echo_json |
| 8 | +from planet.cli.session import CliSession |
| 9 | +from planet.cli.types import BoundingBox, DateTime, Geometry |
| 10 | +from planet.cli.validators import check_geom |
| 11 | +from planet.clients.mosaics import MosaicsClient |
| 12 | + |
| 13 | + |
| 14 | +@asynccontextmanager |
| 15 | +async def client(ctx): |
| 16 | + async with CliSession() as sess: |
| 17 | + cl = MosaicsClient(sess, base_url=ctx.obj['BASE_URL']) |
| 18 | + yield cl |
| 19 | + |
| 20 | + |
| 21 | +include_links = click.option("--links", |
| 22 | + is_flag=True, |
| 23 | + help=("If enabled, include API links")) |
| 24 | + |
| 25 | +name_contains = click.option( |
| 26 | + "--name-contains", |
| 27 | + type=str, |
| 28 | + help=("Match if the name contains text, case-insensitive")) |
| 29 | + |
| 30 | +bbox = click.option('--bbox', |
| 31 | + type=BoundingBox(), |
| 32 | + help=("Region to download as comma-delimited strings: " |
| 33 | + " lon_min,lat_min,lon_max,lat_max")) |
| 34 | + |
| 35 | +interval = click.option("--interval", |
| 36 | + type=str, |
| 37 | + help=("Match this interval, e.g. 1 mon")) |
| 38 | + |
| 39 | +acquired_gt = click.option("--acquired_gt", |
| 40 | + type=DateTime(), |
| 41 | + help=("Imagery acquisition after than this date")) |
| 42 | + |
| 43 | +acquired_lt = click.option("--acquired_lt", |
| 44 | + type=DateTime(), |
| 45 | + help=("Imagery acquisition before than this date")) |
| 46 | + |
| 47 | +geometry = click.option('--geometry', |
| 48 | + type=Geometry(), |
| 49 | + callback=check_geom, |
| 50 | + help=("A geojson geometry to search with. " |
| 51 | + "Can be a string, filename, or - for stdin.")) |
| 52 | + |
| 53 | + |
| 54 | +def _strip_links(resource): |
| 55 | + if isinstance(resource, dict): |
| 56 | + resource.pop("_links", None) |
| 57 | + return resource |
| 58 | + |
| 59 | + |
| 60 | +async def _output(result, pretty, include_links=False): |
| 61 | + if asyncio.iscoroutine(result): |
| 62 | + result = await result |
| 63 | + if not include_links: |
| 64 | + _strip_links(result) |
| 65 | + echo_json(result, pretty) |
| 66 | + else: |
| 67 | + results = [_strip_links(r) async for r in result] |
| 68 | + echo_json(results, pretty) |
| 69 | + |
| 70 | + |
| 71 | +@click.group() # type: ignore |
| 72 | +@click.pass_context |
| 73 | +@click.option('-u', |
| 74 | + '--base-url', |
| 75 | + default=None, |
| 76 | + help='Assign custom base Mosaics API URL.') |
| 77 | +def mosaics(ctx, base_url): |
| 78 | + """Commands for interacting with the Mosaics API""" |
| 79 | + ctx.obj['BASE_URL'] = base_url |
| 80 | + |
| 81 | + |
| 82 | +@mosaics.group() # type: ignore |
| 83 | +def series(): |
| 84 | + """Commands for interacting with Mosaic Series through the Mosaics API""" |
| 85 | + |
| 86 | + |
| 87 | +@command(mosaics, name="contributions") |
| 88 | +@click.argument("name_or_id") |
| 89 | +@click.argument("quad") |
| 90 | +async def quad_contributions(ctx, name_or_id, quad, pretty): |
| 91 | + '''Get contributing scenes for a quad in a mosaic specified by name or ID |
| 92 | +
|
| 93 | + Example: |
| 94 | +
|
| 95 | + planet mosaics contribution global_monthly_2025_04_mosaic 575-1300 |
| 96 | + ''' |
| 97 | + async with client(ctx) as cl: |
| 98 | + item = await cl.get_quad(name_or_id, quad) |
| 99 | + await _output(cl.get_quad_contributions(item), pretty) |
| 100 | + |
| 101 | + |
| 102 | +@command(mosaics, name="info") |
| 103 | +@click.argument("name_or_id", required=True) |
| 104 | +@include_links |
| 105 | +async def mosaic_info(ctx, name_or_id, pretty, links): |
| 106 | + """Get information for a mosaic specified by name or ID |
| 107 | +
|
| 108 | + Example: |
| 109 | +
|
| 110 | + planet mosaics info global_monthly_2025_04_mosaic |
| 111 | + """ |
| 112 | + async with client(ctx) as cl: |
| 113 | + await _output(cl.get_mosaic(name_or_id), pretty, links) |
| 114 | + |
| 115 | + |
| 116 | +@command(mosaics, name="list") |
| 117 | +@name_contains |
| 118 | +@interval |
| 119 | +@acquired_gt |
| 120 | +@acquired_lt |
| 121 | +@include_links |
| 122 | +async def mosaics_list(ctx, |
| 123 | + name_contains, |
| 124 | + interval, |
| 125 | + acquired_gt, |
| 126 | + acquired_lt, |
| 127 | + pretty, |
| 128 | + links): |
| 129 | + """List information for all available mosaics |
| 130 | +
|
| 131 | + Example: |
| 132 | +
|
| 133 | + planet mosaics list --name-contains global_monthly |
| 134 | + """ |
| 135 | + async with client(ctx) as cl: |
| 136 | + await _output( |
| 137 | + cl.list_mosaics(name_contains=name_contains, |
| 138 | + interval=interval, |
| 139 | + acquired_gt=acquired_gt, |
| 140 | + acquired_lt=acquired_lt), |
| 141 | + pretty, |
| 142 | + links) |
| 143 | + |
| 144 | + |
| 145 | +@command(series, name="info") |
| 146 | +@click.argument("name_or_id", required=True) |
| 147 | +@include_links |
| 148 | +async def series_info(ctx, name_or_id, pretty, links): |
| 149 | + """Get information for a series specified by name or ID |
| 150 | +
|
| 151 | + Example: |
| 152 | +
|
| 153 | + planet series info "Global Quarterly" |
| 154 | + """ |
| 155 | + async with client(ctx) as cl: |
| 156 | + await _output(cl.get_series(name_or_id), pretty, links) |
| 157 | + |
| 158 | + |
| 159 | +@command(series, name="list") |
| 160 | +@name_contains |
| 161 | +@interval |
| 162 | +@acquired_gt |
| 163 | +@acquired_lt |
| 164 | +@include_links |
| 165 | +async def series_list(ctx, |
| 166 | + name_contains, |
| 167 | + interval, |
| 168 | + acquired_gt, |
| 169 | + acquired_lt, |
| 170 | + pretty, |
| 171 | + links): |
| 172 | + """List information for available series |
| 173 | +
|
| 174 | + Example: |
| 175 | +
|
| 176 | + planet mosaics series list --name-contains=Global |
| 177 | + """ |
| 178 | + async with client(ctx) as cl: |
| 179 | + await _output( |
| 180 | + cl.list_series( |
| 181 | + name_contains=name_contains, |
| 182 | + interval=interval, |
| 183 | + acquired_gt=acquired_gt, |
| 184 | + acquired_lt=acquired_lt, |
| 185 | + ), |
| 186 | + pretty, |
| 187 | + links) |
| 188 | + |
| 189 | + |
| 190 | +@command(series, name="list-mosaics") |
| 191 | +@click.argument("name_or_id", required=True) |
| 192 | +@click.option("--latest", |
| 193 | + is_flag=True, |
| 194 | + help=("Get the latest mosaic in the series")) |
| 195 | +@acquired_gt |
| 196 | +@acquired_lt |
| 197 | +@include_links |
| 198 | +async def list_series_mosaics(ctx, |
| 199 | + name_or_id, |
| 200 | + acquired_gt, |
| 201 | + acquired_lt, |
| 202 | + latest, |
| 203 | + pretty, |
| 204 | + links): |
| 205 | + """List mosaics in a series specified by name or ID |
| 206 | +
|
| 207 | + Example: |
| 208 | +
|
| 209 | + planet mosaics series list-mosaics global_monthly_2025_04_mosaic |
| 210 | + """ |
| 211 | + async with client(ctx) as cl: |
| 212 | + await _output( |
| 213 | + cl.list_series_mosaics(name_or_id, |
| 214 | + acquired_gt=acquired_gt, |
| 215 | + acquired_lt=acquired_lt, |
| 216 | + latest=latest), |
| 217 | + pretty, |
| 218 | + links) |
| 219 | + |
| 220 | + |
| 221 | +@command(mosaics, name="search") |
| 222 | +@click.argument("name_or_id", required=True) |
| 223 | +@bbox |
| 224 | +@geometry |
| 225 | +@click.option("--summary", |
| 226 | + is_flag=True, |
| 227 | + help=("Get a count of how many quads would be returned")) |
| 228 | +@include_links |
| 229 | +async def list_quads(ctx, name_or_id, bbox, geometry, summary, pretty, links): |
| 230 | + """Search quads in a mosaic specified by name or ID |
| 231 | +
|
| 232 | + Example: |
| 233 | +
|
| 234 | + planet mosaics search global_monthly_2025_04_mosaic --bbox -100,40,-100,41 |
| 235 | + """ |
| 236 | + async with client(ctx) as cl: |
| 237 | + if summary: |
| 238 | + result = cl.summarize_quads(name_or_id, |
| 239 | + bbox=bbox, |
| 240 | + geometry=geometry) |
| 241 | + else: |
| 242 | + result = cl.list_quads(name_or_id, |
| 243 | + minimal=False, |
| 244 | + bbox=bbox, |
| 245 | + geometry=geometry) |
| 246 | + await _output(result, pretty, links) |
| 247 | + |
| 248 | + |
| 249 | +@command(mosaics, name="download") |
| 250 | +@click.argument("name_or_id", required=True) |
| 251 | +@click.option('--output-dir', |
| 252 | + help=('Directory for file download. Defaults to mosaic name'), |
| 253 | + type=click.Path(exists=True, |
| 254 | + resolve_path=True, |
| 255 | + writable=True, |
| 256 | + file_okay=False)) |
| 257 | +@bbox |
| 258 | +@geometry |
| 259 | +async def download(ctx, name_or_id, output_dir, bbox, geometry, **kwargs): |
| 260 | + """Download quads from a mosaic by name or ID |
| 261 | +
|
| 262 | + Example: |
| 263 | +
|
| 264 | + planet mosaics search global_monthly_2025_04_mosaic --bbox -100,40,-100,41 |
| 265 | + """ |
| 266 | + quiet = ctx.obj['QUIET'] |
| 267 | + async with client(ctx) as cl: |
| 268 | + await cl.download_quads(name_or_id, |
| 269 | + bbox=bbox, |
| 270 | + geometry=geometry, |
| 271 | + directory=output_dir, |
| 272 | + progress_bar=not quiet) |
0 commit comments