diff --git a/.gitignore b/.gitignore index d8ed680..0fb8bc5 100644 --- a/.gitignore +++ b/.gitignore @@ -98,3 +98,6 @@ ENV/ /site /.idea/ /raster-foundry-python-client.iml + +# MacOS +.DS_Store diff --git a/examples/Analyses.ipynb b/examples/Analyses.ipynb index 1c616de..3c277d3 100644 --- a/examples/Analyses.ipynb +++ b/examples/Analyses.ipynb @@ -11,6 +11,13 @@ "api = API(refresh_token=refresh_token)" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Getting analyses from the client" + ] + }, { "cell_type": "code", "execution_count": null, @@ -21,7 +28,21 @@ "source": [ "analyses = api.analyses\n", "analysis = analyses[0]\n", - "analyses" + "analysis" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Visualizing analyses" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Comparing to projects" ] }, { @@ -37,25 +58,170 @@ "project.compare(analysis, m)\n", "m" ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Exporting analyses using tile layers" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "from ipyleaflet import DrawControl\n", + "dc = DrawControl()\n", + "\n", + "m = analysis.get_map()\n", + "m.add_control(dc)\n", + "analysis.add_to(m)\n", + "m\n", + "\n", + "# Draw a polygon on the map below" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "last_draw = dc.last_draw\n", + "last_draw" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Calculate a bounding box from the last draw" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def snap_to_360(x_coord):\n", + " \"\"\"Snap an x coordinate to [-180, 180]\n", + " \n", + " Coordinates coming back from the API for some projects can be\n", + " outside this range, and coordinates in the bbox outside this\n", + " range make the export API upset. When it's upset, it returns\n", + " an array with just a single 0 in it, which is not an accurate\n", + " representation of the project normally.\n", + " \"\"\"\n", + " return x_coord - round((x_coord + 180) / 360, 0) * 360\n", + "\n", + "def geom_to_bbox(geom):\n", + " coords = geom['geometry']['coordinates'][0]\n", + " min_x = snap_to_360(min([point[0] for point in coords]))\n", + " min_y = min([point[1] for point in coords])\n", + " max_x = snap_to_360(max([point[0] for point in coords]))\n", + " max_y = max([point[1] for point in coords])\n", + " return ','.join(map(str, [min_x, min_y, max_x, max_y]))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "bbox = geom_to_bbox(last_draw)\n", + "bbox" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Export as GeoTIFF\n", + "\n", + "Note: this example requires\n", + "[`numpy`](http://www.numpy.org/),\n", + "[`matplotlib`](http://matplotlib.org/), and a fairly recent version of\n", + "[`rasterio`](https://mapbox.github.io/rasterio/).\n", + "\n", + "If you don't have them, you can run the cell at the bottom of this notebook,\n", + "provided your pip installation directory is writable." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from rasterio.io import MemoryFile\n", + "import matplotlib.pyplot as plt\n", + "\n", + "thumbnail = analysis.get_thumbnail(bbox=bbox, zoom=8)\n", + "\n", + "with MemoryFile(thumbnail) as memfile:\n", + " with memfile.open() as dataset:\n", + " plt.imshow(dataset.read(1), cmap='RdBu')\n", + " \n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Request an asynchronous export\n", + "\n", + "Asynchronous export is good when you have a large analysis or bounding box, or when you need a high\n", + "zoom level in the resulting geotiff. Creating an asynchronous export requires only a\n", + "bbox and zoom level for this analysis. Created exports run remotely. For examples of what\n", + "you can do with exports, check out the [Exports](./Export.ipynb) notebook." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "export = analysis.create_export(bbox=bbox, zoom=13)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%%bash\n", + "pip install numpy matplotlib rasterio==1.0a12" + ] } ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": "Python 2", "language": "python", - "name": "python3" + "name": "python2" }, "language_info": { "codemirror_mode": { "name": "ipython", - "version": 3 + "version": 2 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.4" + "pygments_lexer": "ipython2", + "version": "2.7.12" } }, "nbformat": 4, diff --git a/examples/Export.ipynb b/examples/Export.ipynb new file mode 100644 index 0000000..36bc205 --- /dev/null +++ b/examples/Export.ipynb @@ -0,0 +1,209 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Exports\n", + "\n", + "This notebooks shows the steps on how we create an export using a project instance." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from rasterfoundry.api import API\n", + "refresh_token = ''\n", + "api = API(refresh_token=refresh_token)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "api.projects" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Create an export directly\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Create a polygon as the mask for the export" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from ipyleaflet import DrawControl\n", + "\n", + "project = api.projects[-1]\n", + "m = project.get_map()\n", + "\n", + "dc = DrawControl()\n", + "m.add_control(dc)\n", + "project.add_to(m)\n", + "m\n", + "\n", + "# Draw a polygon in the map below" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def snap_to_360(x_coord):\n", + " \"\"\"Snap an x coordinate to [-180, 180]\n", + " \n", + " Coordinates coming back from the API for some projects can be\n", + " outside this range, and coordinates in the bbox outside this\n", + " range make the export API upset. When it's upset, it returns\n", + " an array with just a single 0 in it, which is not an accurate\n", + " representation of the project normally.\n", + " \"\"\"\n", + " return x_coord - round((x_coord + 180) / 360, 0) * 360\n", + "\n", + "def geom_to_bbox(geom):\n", + " coords = geom['geometry']['coordinates'][0]\n", + " min_x = snap_to_360(min([point[0] for point in coords]))\n", + " min_y = min([point[1] for point in coords])\n", + " max_x = snap_to_360(max([point[0] for point in coords]))\n", + " max_y = max([point[1] for point in coords])\n", + " return ','.join(map(str, [min_x, min_y, max_x, max_y]))\n", + "\n", + "bbox = geom_to_bbox(dc.last_draw)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Create an export through a project instance" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "project_export = project.create_export(bbox=bbox, zoom=8, raster_size=1000)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Wait until the export is finished" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "completed_export = project_export.wait_for_completion()\n", + "# it will say 'EXPORTED' as the output of this block if the export is finished\n", + "completed_export.export_status" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# When the export is done, we will get a list of download URLs\n", + "project_export.files" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Download and plot the export\n", + "\n", + "Note: this example requires\n", + "[`numpy`](http://www.numpy.org/),\n", + "[`matplotlib`](http://matplotlib.org/), and a fairly recent version of\n", + "[`rasterio`](https://mapbox.github.io/rasterio/).\n", + "\n", + "If you don't have them, you can run the cell at the bottom of this notebook,\n", + "provided your pip installation directory is writable." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from rasterio.io import MemoryFile\n", + "import matplotlib.pyplot as plt\n", + "\n", + "data = project_export.download_file_bytes()\n", + "\n", + "with MemoryFile(data) as memfile:\n", + " with memfile.open() as dataset:\n", + " plt.imshow(dataset.read(1), cmap='viridis')\n", + " \n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Installs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%%bash\n", + "pip install rasterio==1.0a12 matplotlib" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 2", + "language": "python", + "name": "python2" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2", + "version": "2.7.14" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/examples/Projects.ipynb b/examples/Projects.ipynb index 0c8ac96..552807b 100644 --- a/examples/Projects.ipynb +++ b/examples/Projects.ipynb @@ -50,9 +50,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "# Change the index in projects[3] to a value within your list of projects\n", @@ -74,9 +72,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "# Change the index in projects[4] to a value within your list of projects\n", @@ -90,21 +86,21 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Project export" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Export as PNG" + "## Project export\n", + "\n", + "### Synchronous export with the tile server\n", + "\n", + "Synchronous export is good when your project doesn't cover a large\n", + "area, or when you don't need a high zoom level in the resulting\n", + "geotiff.\n", + "\n", + "#### Export as PNG" ] }, { "cell_type": "code", "execution_count": null, "metadata": { - "collapsed": true, "scrolled": true }, "outputs": [], @@ -118,9 +114,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "Image(project.png(bbox, zoom=15))" @@ -130,14 +124,9 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Export as GeoTIFF" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Display in the notebook\n", + "#### Export as GeoTIFF\n", + "\n", + "#### Display in the notebook\n", "\n", "Note: this example requires\n", "[`numpy`](http://www.numpy.org/),\n", @@ -158,9 +147,9 @@ "import matplotlib.pyplot as plt\n", "project = api.projects[3]\n", "bbox = '-121.726057,37.278423,-121.231672,37.377250'\n", - "data = project.geotiff(bbox, zoom=17)\n", + "thumbnail = project.geotiff(bbox, zoom=10)\n", "\n", - "with MemoryFile(data) as memfile:\n", + "with MemoryFile(thumbnail) as memfile:\n", " with memfile.open() as dataset:\n", " plt.imshow(dataset.read(1), cmap='RdBu')\n", " \n", @@ -171,7 +160,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Save as a file" + "#### Save as a file" ] }, { @@ -187,6 +176,27 @@ " outf.write(data)" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Asynchronous export\n", + "\n", + "Asynchronous export is good when you have a large project, or when you need a high\n", + "zoom level in the resulting geotiff. Creating an asynchronous export requires only a\n", + "bbox and zoom level for this project. Created exports run remotely. For examples of what\n", + "you can do with exports, check out the [Exports](./Export.ipynb) notebook." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "export = project.create_export(bbox, zoom=10)" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -197,13 +207,11 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "%%bash\n", - "pip install numpy matplotlib rasterio==1.0a8" + "pip install numpy matplotlib rasterio==1.0a12" ] } ], diff --git a/examples/Uploads.ipynb b/examples/Uploads.ipynb index b0737a6..906334d 100644 --- a/examples/Uploads.ipynb +++ b/examples/Uploads.ipynb @@ -151,7 +151,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython2", - "version": "2.7.12" + "version": "2.7.14" } }, "nbformat": 4, diff --git a/rasterfoundry/api.py b/rasterfoundry/api.py index 5c39eca..2dd448d 100644 --- a/rasterfoundry/api.py +++ b/rasterfoundry/api.py @@ -7,9 +7,10 @@ from bravado.swagger_model import load_file, load_url from simplejson import JSONDecodeError + from .aws.s3 import str_to_file from .exceptions import RefreshTokenException -from .models import Analysis, MapToken, Project +from .models import Analysis, MapToken, Project, Export from .settings import RV_TEMP_URI try: @@ -47,6 +48,7 @@ def __init__(self, refresh_token=None, api_token=None, else: spec = load_file(SPEC_PATH) + self.app_host = host spec['host'] = host spec['schemes'] = [scheme] @@ -147,6 +149,24 @@ def analyses(self): analyses.append(Analysis(analysis, self)) return analyses + @property + def exports(self): + """List exports a user has access to + + Returns: + List[Export] + """ + has_next = True + page = 0 + exports = [] + while has_next: + paginated_exports = self.client.Imagery.get_exports(page=page).result() + has_next = paginated_exports.hasNext + page = paginated_exports.page + 1 + for export in paginated_exports.results: + exports.append(Export(export, self)) + return exports + def get_datasources(self, **kwargs): return self.client.Datasources.get_datasources(**kwargs).result() diff --git a/rasterfoundry/models/__init__.py b/rasterfoundry/models/__init__.py index 41b6aeb..6d1a9a1 100644 --- a/rasterfoundry/models/__init__.py +++ b/rasterfoundry/models/__init__.py @@ -2,3 +2,4 @@ from .map_token import MapToken # NOQA from .upload import Upload # NOQA from .analysis import Analysis # NOQA +from .export import Export # NOQA diff --git a/rasterfoundry/models/analysis.py b/rasterfoundry/models/analysis.py index 85a8780..3f4abcf 100644 --- a/rasterfoundry/models/analysis.py +++ b/rasterfoundry/models/analysis.py @@ -2,6 +2,7 @@ import requests from .. import NOTEBOOK_SUPPORT +from .export import Export from .project import Project from ..decorators import check_notebook from ..exceptions import GatewayTimeoutException @@ -36,16 +37,7 @@ def __init__(self, analysis, api): self.name = analysis.name self.id = analysis.id - def get_export(self, bbox, zoom=10): - """Download this Analysis as a single band tiff - - Args: - bbox (str): Bounding box(formatted as 'x1,y1,x2,y2') for the download - zoom (number): Zoom level for the download - - Returns: - str - """ + def get_thumbnail(self, bbox, zoom, raw=False): export_path = self.EXPORT_TEMPLATE.format(analysis=self.id) request_path = '{scheme}://{host}{export_path}'.format( scheme=self.api.scheme, host=self.api.tile_host, export_path=export_path @@ -57,6 +49,7 @@ def get_export(self, bbox, zoom=10): 'bbox': bbox, 'zoom': zoom, 'token': self.api.api_token, + 'colorCorrect': 'false' if raw else 'true' } ) if response.status_code == requests.codes.gateway_timeout: @@ -67,6 +60,19 @@ def get_export(self, bbox, zoom=10): response.raise_for_status() return response + def create_export(self, bbox, zoom=10, **exportOpts): + """Download this Analysis as a single band tiff + + Args: + bbox (str): Bounding box(formatted as 'x1,y1,x2,y2') for the download + zoom (int): Zoom level for the download + exportOpts (dict): Additional parameters to pass to an async export job + + Returns: + Export + """ + return Export.create_export(self.api, bbox=bbox, zoom=zoom, analysis=self, **exportOpts) + def tms(self, node=None): """Returns a TMS URL for this project diff --git a/rasterfoundry/models/export.py b/rasterfoundry/models/export.py new file mode 100644 index 0000000..adbd188 --- /dev/null +++ b/rasterfoundry/models/export.py @@ -0,0 +1,172 @@ +"""An Export is a job to get underlying geospatial data out of Raster Foundry""" + +import logging +import time + +import requests +from shapely.geometry import mapping, box, MultiPolygon +from bravado import exception + +logger = logging.getLogger(__name__) +logger.addHandler(logging.StreamHandler()) +logger.setLevel('INFO') + + +class Export(object): + def __repr__(self): + return ''.format(self.name) + + def __init__(self, export, api): + """Instantiate a new Export + + Args: + export (Export): generated Export object from specification + api (API): api used to make requests on behalf of the export + """ + + self._export = export + # Someday, exports will have names, but not yet + self.name = export.id + self.id = export.id + self.api = api + self.export_status = export.exportStatus + self.path = export.exportOptions.source + + @property + def files(self): + try: + fnames_res = self.api.client.Imagery.get_exports_uuid_files(uuid=self.id).result() + fnames = filter( + lambda name: name.upper() != 'RFUploadAccessTestFile'.upper(), + fnames_res) + return [ + 'https://{app_host}/api/exports/{export_id}/files/{file_name}'.format( + app_host=self.api.app_host, + export_id=self.id, + file_name=fname) for fname in fnames] + except exception.HTTPNotFound: + logger.info("The files can't be found until an export is completed") + + @classmethod + def poll_export_status(cls, + api, + export_id, + until=['EXPORTED', 'FAILED'], + delay=15): + """Poll the status of an export until it is done + + Note: if you don't include FAILED in the until parameter, polling will continue even + if the export has failed. + + Args: + api (API): API to use for requests + export_id (str): UUID of the export to poll for + until ([str]): list of statuses to indicate completion + delay (int): how long to wait between attempts + + Returns: + Export + """ + + if 'FAILED' not in until: + logger.warn( + 'Not including FAILED in until can result in states in which this ' + 'function will never return. You may have left off FAILED by accident. ' + 'If that is the case, you should include FAILED in until and try again.' + ) + export = api.client.Imagery.get_exports_uuid(uuid=export_id).result() + while export.exportStatus not in until: + time.sleep(delay) + export = api.client.Imagery.get_exports_uuid( + uuid=export_id).result() + return Export(export, api) + + @classmethod + def create_export(cls, + api, + bbox, + zoom, + project=None, + analysis=None, + visibility='PRIVATE', + source=None, + export_type='S3', + raster_size=4000): + """Create an asynchronous export job for a project or analysis + + Only one of project_id or analysis_id should be specified + + Args: + api (API): API to use for requests + bbox (str): comma-separated bounding box of region to export + zoom (int): the zoom level for performing the export + project (Project): the project to export + analysis (Analysis): the analysis to export + visibility (Visibility): what the export's visibility should be set to + source (str): the destination for the exported files + export_type (str): one of 'S3', 'LOCAL', or 'DROPBOX' + raster_size (int): desired tiff size after export, 4000 by default - same as backend + + Returns: + An export object + """ + + if project is not None and analysis is not None: + raise ValueError( + 'Ambiguous export target -- only one of project or analysis should ' + 'be specified') + elif project is None and analysis is None: + raise ValueError( + 'Nothing to export -- one of project or analysis must be specified' + ) + elif project is not None: + update_dict = { + 'projectId': project.id, + 'organizationId': project._project.organizationId + } + else: + update_dict = { + 'toolRunId': analysis.id, + 'organizationId': analysis._analysis.organizationId + } + + box_poly = MultiPolygon([box(*map(float, bbox.split(',')))]) + + export_create = { + 'exportOptions': { + 'mask': mapping(box_poly), + 'resolution': zoom, + 'rasterSize': raster_size + }, + 'projectId': None, + 'exportStatus': 'TOBEEXPORTED', + 'exportType': export_type, + 'visibility': visibility, + 'toolRunId': None, + 'organizationId': None + } + export_create.update(update_dict) + return Export( + api.client.Imagery.post_exports(Export=export_create).result(), + api) + + def wait_for_completion(self): + """Wait until this export succeeds or fails, returning the completed export + + Returns: + Export + """ + return self.__class__.poll_export_status(self.api, self.id) + + def download_file_bytes(self, index=0): + """Download the exported file from this export to memory + + Args: + index (int): which of this export's files to download + + Returns: + a binary file + """ + resp = requests.get(self.files[index], params={'token': self.api.api_token}) + resp.raise_for_status() + return resp.content diff --git a/rasterfoundry/models/project.py b/rasterfoundry/models/project.py index b1f77fe..fd78ff8 100644 --- a/rasterfoundry/models/project.py +++ b/rasterfoundry/models/project.py @@ -1,15 +1,17 @@ """A Project is a collection of zero or more scenes""" -import requests -import uuid -import json import copy +import json +import uuid from datetime import date, datetime +import requests + +from .export import Export +from .map_token import MapToken from .. import NOTEBOOK_SUPPORT +from ..aws.s3 import file_to_str, str_to_file from ..decorators import check_notebook from ..exceptions import GatewayTimeoutException -from .map_token import MapToken -from ..aws.s3 import str_to_file, file_to_str from ..utils import get_all_paginated if NOTEBOOK_SUPPORT: @@ -90,19 +92,7 @@ def get_map_token(self): if resp.results: return MapToken(resp.results[0], self.api) - def get_export(self, bbox, zoom=10, export_format='png', raw=False): - """Download this project as a file - - PNGs will be returned if the export_format is anything other than tiff - - Args: - bbox (str): Bounding box (formatted as 'x1,y1,x2,y2') for the download - export_format (str): Requested download format - - Returns: - str - """ - + def get_thumbnail(self, bbox, zoom, export_format, raw): headers = self.api.http.session.headers.copy() headers['Accept'] = 'image/{}'.format( export_format @@ -133,6 +123,20 @@ def get_export(self, bbox, zoom=10, export_format='png', raw=False): response.raise_for_status() return response + def create_export(self, bbox, zoom=10, raster_size=4000): + """Create an export job for this project + + Args: + bbox (str): Bounding box (formatted as 'x1,y1,x2,y2') for the download + zoom (int): Zoom level for the download + raster_size (int): desired tiff size after export, 4000 by default - same as backend + + Returns: + Export + """ + return Export.create_export( + self.api, bbox=bbox, zoom=zoom, project=self, raster_size=raster_size) + def geotiff(self, bbox, zoom=10, raw=False): """Download this project as a geotiff @@ -146,7 +150,7 @@ def geotiff(self, bbox, zoom=10, raw=False): str """ - return self.get_export(bbox, zoom, 'tiff', raw).content + return self.get_thumbnail(bbox, zoom, 'tiff', raw).content def png(self, bbox, zoom=10, raw=False): """Download this project as a png @@ -161,7 +165,7 @@ def png(self, bbox, zoom=10, raw=False): str """ - return self.get_export(bbox, zoom, 'png', raw).content + return self.get_thumbnail(bbox, zoom, 'png', raw).content def tms(self): """Return a TMS URL for a project""" diff --git a/rasterfoundry/spec.yml b/rasterfoundry/spec.yml index 6d2226c..b668950 100644 --- a/rasterfoundry/spec.yml +++ b/rasterfoundry/spec.yml @@ -1887,11 +1887,17 @@ paths: in: path type: string description: The filename of the file the user wishes to download. Filenames of an export need to first be fetched. + - name: token + required: true + in: query + type: string + description: API token responses: 200: description: The content of the reqested file schema: - type: file + type: string + format: binary 404: description: | UUID does not reference an export that exists, the user has access to, and has finished successfully. The filename may not exist @@ -2371,14 +2377,11 @@ parameters: type: string required: false enum: - - CREATED + - NOTEXPORTED + - TOBEEXPORTED - EXPORTING - EXPORTED - - QUEUED - - PROCESSING - - COMPLETE - FAILED - - ABORTED source: name: source description: Feed source URI @@ -3642,23 +3645,15 @@ definitions: projectId: type: string format: uuid - sceneIds: - type: array - items: - type: string - format: uuid exportStatus: type: string description: Status of export enum: - - CREATED + - NOTEXPORTED + - TOBEEXPORTED - EXPORTING - EXPORTED - - QUEUED - - PROCESSING - - COMPLETE - FAILED - - ABORTED exportType: type: string description: Source of exports @@ -3684,20 +3679,6 @@ definitions: - $ref: '#/definitions/BaseModel' - $ref: '#/definitions/UserTrackingMixin' - $ref: '#/definitions/ExportCreate' - - type: object - properties: - exportStatus: - type: string - description: Status of export - enum: - - CREATED - - EXPORTING - - EXPORTED - - QUEUED - - PROCESSING - - COMPLETE - - FAILED - - ABORTED ExportPaginated: allOf: - $ref: '#/definitions/PaginatedResponse' @@ -3725,9 +3706,6 @@ definitions: crs: type: integer description: epsg projection code - mask: - type: object - description: GeoJSON multipolygon stitch: type: boolean description: stitch tiles into a single geotiff if possible diff --git a/setup.py b/setup.py index 20d8c0b..59f5a65 100644 --- a/setup.py +++ b/setup.py @@ -26,7 +26,8 @@ 'requests >= 2.9.1', 'bravado >= 8.4.0', 'boto3 >= 1.4.4', - 'future >= 0.16.0' + 'future >= 0.16.0', + 'shapely >= 1.6.4post1' ], extras_require={ 'notebook': [