OWM provides an API for Agricultural monitoring that provides soil data, satellite imagery, etc.

The first thing you need to do to get started with it is to create a *polygon* and store it on the OWM Agro API.
PyOWM will give you this new polygon's ID and you will use it to invoke data queries upon that polygon.

Eg: you can look up satellite imagery, weather data, historical NDVI for that specific polygon.

Read further on to get more details.


## OWM website technical reference
 - [https://agromonitoring.com/api](hhttps://agromonitoring.com/api)


## AgroAPI Manager object

In order to do any kind of operations against the OWM Agro API, you need to obtain a `pyowm.agro10.agro_manager.AgroManager`
instance from the main OWM. You'll need your API Key for that:

```python
import pyowm
owm = pyowm.OWM('your-API-key')
mgr = owm.agro_manager()
```

Read on to discover what you can do with it.


## Polygon API operations

A polygon represents an area on a map upon which you can issue data queries. Each polygon has a unique ID, an optional
name and links back to unique OWM ID of the User that owns that polygon. Each polygon has an area that is expressed 
in acres, but you can also get it in squared kilometers:

```python
pol                  # this is a pyowm.agro10.polygon.Polygon instance
pol.id               # ID
pol.area             # in hacres
pol.area_km          # in sq kilometers
pol.user_id          # owner ID
```

Each polygon also carries along the `pyowm.utils.geo.Polygon` object that represents the geographic polygon and the
`pyowm.utils.geo.Point` object that represents the baricentre of the polygon:

```python
geopol = pol.geopolygon   # pyowm.utils.geo.Polygon object
point = pol.center        # pyowm.utils.geo.Point object
```

### Reading Polygons
You can either get all of the Polygons you've created on the Agro API or easily get single polygons by specifying
their IDs:

```python
list_of_polygons = mgr.get_polygons()
a_polygon = mgr.get_polygon('5abb9fb82c8897000bde3e87')
```


### Creating Polygons
Creating polygons is easy: you just need to create a `pyowm.utils.geo.Polygon` instance that describes the coordinates
of the polygon you want to create on the Agro API. Then you just need to pass it (along with an optional name) to the
Agro Manager object:

```python

# first create the pyowm.utils.geo.Polygon instance that represents the area (here, a triangle)
from pyowm.utils.geo import Polygon as GeoPolygon
gp = GeoPolygon([[
        [-121.1958, 37.6683],
        [-121.1779, 37.6687],
        [-121.1773, 37.6792],
        [-121.1958, 37.6683]]])

# use the Agro Manager to create your polygon on the Agro API 
the_new_polygon = mgr.create_polygon(gp, 'my new shiny polygon')

# the new polygon has an ID and a user_id
the_new_polygon.id
the_new_polygon.user_id
```

You get back a `pyowm.agro10.polygon.Polygon` instance and you can use its ID to operate this new polygon on all the
other Agro API methods! 

### Updating a Polygon
Once you've created a polygon, you can only change its mnemonic name, as the rest of its parameters cannot
be changed by the user. In order to do it:

```python
my_polygon.name  # "my new shiny polygon"
my_polygon.name = "changed name"
mgr.update_polygon(my_polygon)
```

### Deleting a Polygon
Delete a polygon with

```python
mgr.delete_polygon(my_polygon)
```

Remember that when you delete a polygon, there is no going back!


## Soil data API Operations

Once you've defined a polygon, you can easily get soil data upon it. Just go with:

```python
soil = mgr.soil_data(polygon)
```

`Soil` is an entity of type `pyowm.agro10.soil.Soil` and is basically a wrapper around a Python dict reporting
the basic soil information on that polygon:
```python
soil.polygon_id                         # str           
soil.reference_time(timeformat='unix')  # can be: int for UTC Unix time ('unix'), 
                                        # ISO8601-formatted str for 'iso' or 
                                        # datetime.datetime for 'date'
soil.surface_temp(unit='kelvin')        # float (unit can be: 'kelvin', 'celsius' or 'fahrenheit')
soil.ten_cm_temp(unit='kelvin')         # float (Kelvins, measured at 10 cm depth) - unit same as for above
soil.moisture                           # float (m^3/m^3)
```

Soil data is updated twice a day.


## Satellite Imagery API Operations

This is the real meat in Agro API: the possibility to obtain **satellite imagery** right upon your polygons!

### Overview

Satellite Imagery comes in 3 formats:
  - **PNG images**
  - **PNG tiles** (variable zoom level)
  - **GeoTIFF images**

Tiles can be retrieved by specifying a proper set of tile coordinates (x, y) and a zoom level: please refer to PyOWM's 
Map Tiles client documentation for further insights.

When we say that imagery is upon a polygon we mean that the polygon is fully contained in the scene that was acquired by
the satellite.

Each image comes with a **preset**: a preset tells how the acquired scene has been post-processed, eg: image has been
put in false colors or image contains values of the Enhanced Vegetation Index (EVI) calculated on the scene

Imagery is provided by the Agro API for different **satellites** 

Images that you retrieve from the Agro API are `pyowm.agroapi10.imagery.SatelliteImage` instances, and they **contain both
image's data and metadata**.

You can download NDVI images using several **color palettes** provided by the Agro API, for easier processing on your side.


### Operations summary

Once you've defined a polygon, you can:
  - **search for available images** upon the polygon and taken in a specific time frame. The search can be performed with 
     multiple filters (including: satellite symbol, image type, image preset, min/max resolution, minx/max cloud coverage, ...)
     and returns search results, each one being *metadata* for a specific image.
  - from those metadata, **download an image**, be it a regular scene or a tile, optionally specifying a color palette for NDVI ones
  - if your image has EVI or NDVI presets, you can **query for its statistics**: these include min/max/median/p25/p75 values
    for the corresponding index
    
**A concrete example**: we want to acquire all NDVI GeoTIFF images acquired by Landsat 8 from July 18, 2017 to October 26, 2017;
then we want to get stats for one such image and to save it to a local file.


```python
from pyowm.commons.enums import ImageTypeEnum
from pyowm.agroapi10.enums import SatelliteEnum, PresetEnum

pol_id = '5abb9fb82c8897000bde3e87'  # your polygon's ID
acq_from = 1500336000                # 18 July 2017
acq_to = 1508976000                  # 26 October 2017
img_type = ImageTypeEnum.GEOTIFF     # the image format type
preset = PresetEnum.NDVI    # the preset
sat = SatelliteEnum.LANDSAT_8.symbol # the satellite


# the search returns images metadata (in the form of `MetaImage` objects)
results = mgr.search_satellite_imagery(pol_id, acq_from, acq_to, img_type=img_type, preset=preset, None, None, acquired_by=sat)

# download all of the images
satellite_images = [mgr.download_satellite_image(result) for result in results]

# get stats for the first image
sat_img = satellite_images[0]
stats_dict = mgr.stats_for_satellite_image(sat_img)

# ...satellite images can be saved to disk
sat_img.persist('/path/to/my/folder/sat_img.tif')
```

Let's see in detail all of the imagery-based operations.


### Searching images

Search available imagery upon your polygon by specifying at least a mandatory time window, with from and to timestamps
expressed as UNIX UTC timestamps:

```python

pol_id = '5abb9fb82c8897000bde3e87'  # your polygon's ID
acq_from = 1500336000                # 18 July 2017
acq_to = 1508976000                  # 26 October 2017

# the most basic search ever: search all available images upon the polygon in the specified time frame 
metaimages_list = mgr.search_satellite_imagery(pol_id, acq_from, acq_to)

```

What you will get back is actually metadata for the actual imagery, not data.

The function call will return **a list of `pyowm.agroapi10.imagery.MetaImage` instances, each
one being a bunch of metadata relating to one single satellite image**.

Keep these objects, as you will need them in order to download the corresponding satellite images from the Agro API:
think of them such as descriptors for the real images.

But let's get back to search! Search is a parametric affair... **you can specify many more filters**:
  
  - the image format type (eg. PNG, GEOTIFF)
  - the image preset (eg. false color, EVI)
  - the satellite that acquired the image (you need to specify its symbol)
  - the px/m resolution range for the image (you can specify a minimum value, a maximum value or both of them)
  - the % of cloud coverage on the acquired scene (you can specify a minimum value, a maximum value or both of them)
  - the % of valid data coverage on the acquired scene (you can specify a minimum value, a maximum value or both of them)

Sky is the limit...

As regards image type, image preset and satellite filters please refer to subsequent sections explaining the supported
values.

Examples of search:

```python
from pyowm.commons.enums import ImageTypeEnum
from pyowm.agroapi10.enums import SatelliteEnum, PresetEnum 


# search all Landsat 8 images in the specified time frame
results = mgr.search_satellite_imagery(pol_id, acq_from, acq_to, acquired_by=SatelliteEnum.LANDSAT_8.symbol)

# search all GeoTIFF images in the specified time frame                                     
results = mgr.search_satellite_imagery(pol_id, acq_from, acq_to, img_type=ImageTypeEnum.GEOTIFF)

# search all NDVI images acquired by Sentinel 2 in the specified time frame
results = mgr.search_satellite_imagery(pol_id, acq_from, acq_to, acquired_by=SatelliteEnum.SENTINEL_2.symbol,
                                       preset=PresetEnum.NDVI)

# search all PNG images in the specified time frame with a max cloud coverage of 1% and a min valid data coverage of 98%
results = mgr.search_satellite_imagery(pol_id, acq_from, acq_to, img_type=ImageTypeEnum.PNG, 
                                       max_cloud_coverage=1, min_valid_data_coverage=98)

# search all true color PNG images in the specified time frame, acquired by Sentinel 2, with a range of metric resolution
# from 4 to 16 px/m, and with at least 90% of valid data coverage
results = mgr.search_satellite_imagery(pol_id, acq_from, acq_to, img_type=ImageTypeEnum.PNG, preset=PresetEnum.TRUE_COLOR,
                                       min_resolution=4, max_resolution=16, min_valid_data_coverage=90)
```

So, what metadata can be extracted by a `MetaImage` object? Here we go:

```python
metaimage.polygon_id                 # the ID of the polygon upon which the image is taken
metaimage.url                        # the URL the actual satellite image can be fetched from
metaimage.preset                     # the satellite image preset
metaimage.image_type                 # the satellite image format type
metaimage.satellite_name             # the name of the satellite that acquired the image
metaimage.acquisition_time('unix')   # the timestamp when the image was taken (can be specified using: 'iso', 'unix' and 'date')        
metaimage.valid_data_percentage      # the percentage of valid data coverage on the image
metaimage.cloud_coverage_percentage  # the percentage of cloud coverage on the image
metaimage.sun_azimuth                # the sun azimuth angle at scene acquisition time
metaimage.sun_elevation              # the sun zenith angle at scene acquisition time
metaimage.stats_url                  # if the image is EVI or NDVI, this is the URL where index statistics can be retrieved (see further on for details)
```


### Download an image

Once you've got your metaimages ready, you can download the actual satellite images.

In order to download, you must specify to the Agro API manager object at least the desired metaimage to fetch. If you're
downloading a tile, you must specify tile coordinates (x, y, and zoom level): these are mandatory, and if you forget
to provide them you'll get an `AssertionError`.

Optionally, you can specify a color palette - but this will be significant only if you're downloading an image with NDVI 
preset (otherwise the palette parameter will be safely ignored) - please see further on for reference.

Once download is complete, you'll get back a `pyowm.agroapi10.imagery.SatelliteImage` object (more on this in a while).

Here are some examples:

```python
from pyowm.agroapi10.enums import PaletteEnum

# Download a NDVI image
ndvi_metaimage   # metaimage for a NDVI image
bnw_sat_image = mgr.download_satellite_image(ndvi_metaimage, preset=PaletteEnum.BLACK_AND_WHITE)
green_sat_image =  mgr.download_satellite_image(ndvi_metaimage, preset=PaletteEnum.GREEN)

# Download a tile
tile_metaimage   # metaimage for a tile
tile_image = mgr.download_satellite_image(tile_metaimage, x=2, y=3, zoom=5)
tile_image = mgr.download_satellite_image(tile_metaimage)   # AssertionError (x, y and zoom are missing!)
```

Downloaded satellite images contain both binary image data and and embed *the original `MetaImage` object describing
image metadata*. Furthermore, you can query for the download time of a satellite image, and for its related color palette:

```python

# Get satellite image download time - you can as usual specify: 'iso', 'date' and 'unix' time formats
bnw_sat_image.downloaded_on('iso')  #  '2017-07-18 14:08:23+00:00'

# Get its palette
bnw_sat_image.palette               #  '2'

# Get satellite image's data and metadata
bnw_sat_image.data                  # this returns a `pyowm.commons.image.Image` object or a
                                    # `pyowm.commons.tile.Tile` object depending on the satellite image
metaimage = bnw_sat_image.metadata  # this gives a `MetaImage` subtype object

# Use the Metaimage object as usual...
metaimage.polygon_id
metaimage.preset,
metaimage.satellite_name
metaimage.acquisition_time
```

You can also save satellite images to disk - it's as easy as:

```python
bnw_sat_image.persist('C:\myfolder\myfile.png')
```

### Querying for NDVI and EVI image stats

NDVI and EVI preset images have an extra blessing: you can query for statistics about the image index.

Once you've downloaded such satellite images, you can query for stats and get back a data dictionary for each of them:

```python
ndvi_metaimage   # metaimage for a NDVI image

# download it
bnw_sat_image = mgr.download_satellite_image(ndvi_metaimage, preset=PaletteEnum.BLACK_AND_WHITE)

# query for stats
stats_dict = mgr.stats_for_satellite_image(bnw_sat_image)
```

Stats dictionaries contain:
  - `std`: the standard deviation of the index
  - `p25`: the first quartile value of the index
  - `num`: the number of pixels in the current polygon
  - `min`: the minimum value of the index
  - `max`: the maximum value of the index
  - `median`: the median value of the index
  - `p75`: the third quartile value of the index
  - `mean`: the average value of the index

*What if you try to get stats for a non-NDVI or non-EVI image*? A `ValueError` will be raised!


### Supported satellites

Supported satellites are provided by the `pyowm.agroapi10.enums.SatelliteEnum` enumerator which returns
`pyowm.commons.databoxes.Satellite` objects:

```python
from pyowm.agroapi10.enums import SatelliteEnum

sat = SatelliteEnum.SENTINEL_2
sat.name   # 'Sentinel-2'
sat.symbol # 's2'
```

Currently only Landsat 8 and Sentinel 2 satellite imagery is available


### Supported presets
Supported presets are provided by the `pyowm.agroapi10.enums.PresetEnum` enumerator which returns strings, each
one representing an image preset:
 
 ```python
from pyowm.agroapi10.enums import PresetEnum

PresetEnum.TRUE_COLOR  # 'truecolor'
```

Currently these are the supported presets: true color, false color, NDVI and EVI


### Supported image types
Supported image types are provided by the `pyowm.commons.databoxes.ImageTypeEnum` enumerator which returns
`pyowm.commons.databoxes.ImageType` objects:
 
 ```python
from pyowm.agroapi10.enums import ImageTypeEnum

png_type = ImageTypeEnum.PNG
geotiff_type = ImageTypeEnum.GEOTIFF
png_type.name       # 'PNG'
png_type.mime_type  # 'image/png'
```

Currently only PNG and GEOTIFF imagery is available


### Supported color palettes
Supported color palettes are provided by the `pyowm.agroapi10.enums.PaletteEnum` enumerator which returns strings,
each one representing a color palette for NDVI images:

 ```python
from pyowm.agroapi10.enums import PaletteEnum

PaletteEnum.CONTRAST_SHIFTED  # '3'
```

As said, palettes only apply to NDVI images: if you try to specify palettes when downloading images with different
presets (eg. false color images), *nothing will happen*.

The default Agro API color palette is `PaletteEnum.GREEN` (which is `1`): if you don't specify any palette at all when 
downloading NDVI images, they will anyway be returned with this palette.

As of today green, black and white and two contrast palettes (one continuous and one continuous but shifted) are
supported by the Agro API. Please check the documentation for palettes' details, including control points.
