Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
AleksMat committed Oct 7, 2018
2 parents e624a88 + 1bd2fc7 commit 4f648b2
Show file tree
Hide file tree
Showing 19 changed files with 347 additions and 221 deletions.
2 changes: 1 addition & 1 deletion MANIFEST.in
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
include requirements*.txt

include README.md
include LICENSE
include LICENSE.md
include setup.py
include sentinelhub/config.json
17 changes: 1 addition & 16 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,7 @@ help:
@echo "Use 'make upload' to reset config.json and upload the package to PyPi"

reset-config:
$(CONFIG) --instance_id "" \
--aws_access_key_id "" \
--aws_secret_access_key "" \
--ogc_base_url "https://services.sentinel-hub.com/ogc/" \
--gpd_base_url "http://service.geopedia.world/" \
--aws_metadata_base_url "https://roda.sentinel-hub.com/" \
--aws_s3_l1c_bucket "sentinel-s2-l1c" \
--aws_s3_l2a_bucket "sentinel-s2-l2a" \
--opensearch_url "http://opensearch.sentinel-hub.com/resto/api/collections/Sentinel2/" \
--max_wfs_records_per_query 100 \
--max_opensearch_records_per_query 500 \
--default_start_date "1985-01-01" \
--max_download_attempts 4 \
--download_sleep_time 5 \
--download_timeout_seconds 120

$(CONFIG) --reset

upload: reset-config
$(PYTHON) setup.py sdist
Expand Down
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
[![](https://img.shields.io/pypi/l/sentinelhub.svg)](https://github.com/sentinel-hub/sentinelhub-py/blob/master/LICENSE.md)
[![Package version](https://badge.fury.io/py/sentinelhub.svg)](https://pypi.org/project/sentinelhub/)
[![Supported Python versions](https://img.shields.io/pypi/pyversions/sentinelhub.svg?style=flat-square)](https://pypi.org/project/sentinelhub/)
[![Build status](https://travis-ci.org/sentinel-hub/sentinelhub-py.svg?branch=master)](https://travis-ci.org/sentinel-hub/sentinelhub-py)
[![Docs status](https://readthedocs.org/projects/sentinelhub-py/badge/?version=latest)](http://sentinelhub-py.readthedocs.io/en/latest/)

[![Overall downloads](http://pepy.tech/badge/sentinelhub)](http://pepy.tech/project/sentinelhub)
[![Last month downloads](https://img.shields.io/badge/dynamic/json.svg?label=downloads&url=https%3A%2F%2Fpypistats.org%2Fapi%2Fpackages%2Fsentinelhub%2Frecent%3Fperiod%3Dmonth&query=%24.data.last_month&colorB=blue&suffix=%2fmonth)](https://pypistats.org/packages/sentinelhub)
[![](https://img.shields.io/pypi/l/sentinelhub.svg)](https://github.com/sentinel-hub/sentinelhub-py/blob/master/LICENSE.md)

# Description

Expand Down
1 change: 1 addition & 0 deletions docs/source/time_utils.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ Utility functions for processing time/date formats.
.. autofunction:: get_current_date
.. autofunction:: is_valid_time
.. autofunction:: parse_time
.. autofunction:: parse_time_interval
2 changes: 1 addition & 1 deletion sentinelhub/_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
Version of sentinelhub package
"""

__version__ = "2.4.2"
__version__ = "2.4.3"
20 changes: 12 additions & 8 deletions sentinelhub/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,9 @@ def config_options(func):

@click.command()
@click.option('--show', is_flag=True, default=False, help='Show current configuration')
@click.option('--reset', is_flag=True, default=False, help='Reset configuration to initial state')
@config_options
def config(show, **params):
def config(show, reset, **params):
"""Inspect and configure parameters in your local sentinelhub configuration file
\b
Expand All @@ -88,7 +89,10 @@ def config(show, **params):
sentinelhub.config --max_download_attempts 5 --download_sleep_time 20 --download_timeout_seconds 120
"""
sh_config = SHConfig()
updated_params = {}

if reset:
sh_config.reset()

for param, value in params.items():
if value is not None:
try:
Expand All @@ -100,13 +104,13 @@ def config(show, **params):
value = False
if getattr(sh_config, param) != value:
setattr(sh_config, param, value)
updated_params[param] = value
if updated_params:
sh_config.save()

for param in SHConfig().get_params():
if param in updated_params:
value = updated_params[param]
old_config = SHConfig()
sh_config.save()

for param in sh_config.get_params():
if sh_config[param] != old_config[param]:
value = sh_config[param]
if isinstance(value, str):
value = "'{}'".format(value)
click.echo("The value of parameter '{}' was updated to {}".format(param, value))
Expand Down
41 changes: 38 additions & 3 deletions sentinelhub/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@
- BBox, represent a bounding box in a given CRS
"""

import shapely.geometry

from .constants import CRS
from .geo_utils import transform_point


class BBox:
Expand Down Expand Up @@ -82,13 +85,25 @@ def get_crs(self):
"""
return self.crs

def transform(self, target_crs):
""" Transforms BBox from current CRS to target CRS
:param target_crs: target CRS
:type target_crs: constants.CRS
:return: bounding box in target CRS
:rtype: common.BBox
"""
self.min_x, self.min_y = transform_point(self.get_lower_left(), self.crs, target_crs)
self.max_x, self.max_y = transform_point(self.get_upper_right(), self.crs, target_crs)
self.crs = target_crs

def get_polygon(self, reverse=False):
""" Returns a list of coordinates of 5 points describing a polygon. Points are listed in clockwise order, first
point is the same as the last.
:param reverse: True if x and y coordinates should be switched and False otherwise
:type reverse: bool
:return: [[x_1, y_1], ... , [x_5, y_5]]
:return: `[[x_1, y_1], ... , [x_5, y_5]]`
:rtype: list(list(float))
"""
polygon = [[self.min_x, self.min_y],
Expand All @@ -101,6 +116,26 @@ def get_polygon(self, reverse=False):
polygon[i] = point[::-1]
return polygon

def get_geojson(self):
""" Returns polygon geometry in GeoJSON format
:return: A dictionary in GeoJSON format
:rtype: dict
"""
return {'type': 'Polygon',
'crs': {'type': 'name',
'properties': {'name': 'urn:ogc:def:crs:EPSG::{}'.format(self.get_crs().value)}},
'coordinates': [self.get_polygon()]
}

def get_geometry(self):
""" Returns polygon geometry in shapely format
:return: A polygon in shapely format
:rtype: shapely.geometry.polygon.Polygon
"""
return shapely.geometry.Polygon(self.get_polygon())

def get_partition(self, num_x=1, num_y=1):
""" Partitions bounding box into smaller bounding boxes of the same size.
Expand Down Expand Up @@ -179,7 +214,7 @@ def _tuple_from_list_or_tuple(bbox):
def _tuple_from_str(bbox):
""" Parses a string of numbers separated by any combination of commas and spaces
:param bbox: e.g. str of the form 'min_x ,min_y max_x, max_y'
:param bbox: e.g. str of the form `min_x ,min_y max_x, max_y`
:return: tuple (min_x,min_y,max_x,max_y)
"""
return tuple([float(s) for s in bbox.replace(',', ' ').split() if s])
Expand All @@ -199,6 +234,6 @@ def _tuple_from_bbox(bbox):
""" Converts a BBox instance into a tuple
:param bbox: An instance of the BBox type
:return: tuple (min_x,min_y,max_x,max_y)
:return: tuple (min_x, min_y, max_x, max_y)
"""
return bbox.get_lower_left() + bbox.get_upper_right()
91 changes: 67 additions & 24 deletions sentinelhub/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
Module that collects configuration data from config.json
"""

import os.path
import os
import json
from collections import OrderedDict

Expand Down Expand Up @@ -42,21 +42,21 @@ class _SHConfig:
Private class.
"""
CONFIG_PARAMS = OrderedDict([
('instance_id', str),
('aws_access_key_id', str),
('aws_secret_access_key', str),
('ogc_base_url', str),
('gpd_base_url', str),
('aws_metadata_base_url', str),
('aws_s3_l1c_bucket', str),
('aws_s3_l2a_bucket', str),
('opensearch_url', str),
('max_wfs_records_per_query', int),
('max_opensearch_records_per_query', int),
('default_start_date', str),
('max_download_attempts', int),
('download_sleep_time', int),
('download_timeout_seconds', int)
('instance_id', ''),
('aws_access_key_id', ''),
('aws_secret_access_key', ''),
('ogc_base_url', 'https://services.sentinel-hub.com/ogc/'),
('gpd_base_url', 'http://service.geopedia.world/'),
('aws_metadata_base_url', 'https://roda.sentinel-hub.com/'),
('aws_s3_l1c_bucket', 'sentinel-s2-l1c'),
('aws_s3_l2a_bucket', 'sentinel-s2-l2a'),
('opensearch_url', 'http://opensearch.sentinel-hub.com/resto/api/collections/Sentinel2/'),
('max_wfs_records_per_query', 100),
('max_opensearch_records_per_query', 500),
('default_start_date', '1985-01-01'),
('max_download_attempts', 4),
('download_sleep_time', 5),
('download_timeout_seconds', 120)
])

def __init__(self):
Expand All @@ -65,16 +65,16 @@ def __init__(self):

def _check_configuration(self, config):
"""
Checks if configuration file has contains all keys.
Checks if configuration file contains all keys.
:param config: configuration dictionary read from ``config.json``
:type config: dict
"""

for param in self.CONFIG_PARAMS:
if param not in config:
raise ValueError("Configuration file does not contain '%s' parameter." % param)
for param, param_type in self.CONFIG_PARAMS.items():
for param, default_param in self.CONFIG_PARAMS.items():
param_type = type(default_param)
if not isinstance(config[param], param_type):
raise ValueError("Value of parameter '{}' must be of type {}".format(param, param_type.__name__))
if config['max_wfs_records_per_query'] > 100:
Expand Down Expand Up @@ -136,21 +136,26 @@ def __init__(self):
if not SHConfig._instance:
SHConfig._instance = self._SHConfig()

for prop in self._instance.CONFIG_PARAMS:
setattr(self, prop, getattr(self._instance, prop))

def __getattr__(self, name):
""" This is called only if the class doesn't have the attribute itself
"""
return getattr(self._instance, name)

def __getitem__(self, name):
return getattr(self._instance, name)
return getattr(self, name)

def __dir__(self):
return sorted(list(dir(super())) + list(self._instance.CONFIG_PARAMS))

def __str__(self):
return json.dumps(self._instance.get_config(), indent=2)
return json.dumps(self.get_config_dict(), indent=2)

def save(self):
"""Method that saves configuration parameter changes from instance of SHConfig class to global config class and
to ``config.json`` file.
to `config.json` file.
Example of use case
``my_config = SHConfig()`` \n
Expand All @@ -165,6 +170,36 @@ def save(self):
if is_changed:
self._instance.save_configuration()

def reset(self, params=...):
"""
Resets configuration class to initial values. Use SHConfig.save() method in order to save this change.
:param params: Parameters which will be reset. Parameters can be specified with a list of names, e.g.
``['instance_id', 'aws_access_key_id', 'aws_secret_access_key']``, or as a single name, e.g.
``'ogc_base_url'``. By default all parameters will be reset and default value is ``Ellipsis``.
:type params: Ellipsis or list(str) or str
"""
if params is ...:
params = self.get_params()
if isinstance(params, str):
self._reset_param(params)
elif isinstance(params, (list, tuple)):
for param in params:
self._reset_param(param)
else:
raise ValueError('Parameters must be specified in form of a list of strings or as a single string, instead '
'got {}'.format(params))

def _reset_param(self, param):
""" Resets a single parameter
:param param: A configuration parameter
:type param: str
"""
if param not in self._instance.CONFIG_PARAMS:
raise ValueError("Cannot reset unknown parameter '{}'".format(param))
setattr(self, param, self._instance.CONFIG_PARAMS[param])

def get_params(self):
"""Returns a list of parameter names
Expand All @@ -173,18 +208,26 @@ def get_params(self):
"""
return list(self._instance.CONFIG_PARAMS)

def get_config_dict(self):
""" Get a dictionary representation of `SHConfig` class
:return: A dictionary with configuration parameters
:rtype: OrderedDict
"""
return OrderedDict((prop, getattr(self, prop)) for prop in self._instance.CONFIG_PARAMS)

def get_config_location(self):
""" Returns location of configuration file on disk
:return: File path of config.json file
:return: File path of `config.json` file
:rtype: str
"""
return self._instance.get_config_file()

def is_eocloud_ogc_url(self):
""" Checks if base OGC URL is set to eocloud URL
:return: True if 'eocloud' string is in base OGC URL else False
:return: ``True`` if 'eocloud' string is in base OGC URL else ``False``
:rtype: bool
"""
return 'eocloud' in self.ogc_base_url
40 changes: 30 additions & 10 deletions sentinelhub/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -473,22 +473,42 @@ def has_value(cls, value):
"""
return any(value == item.value for item in cls)

@staticmethod
def get_string(fmt):
def get_string(self):
""" Get file format as string
:param fmt: MimeType enum constant
:type fmt: Enum constant
:return: String describing the file format
:rtype: str
"""
if fmt in [MimeType.TIFF_d8, MimeType.TIFF_d16, MimeType.TIFF_d32f]:
return 'image/{}'.format(fmt.value)
if fmt is MimeType.JP2:
if self in [MimeType.TIFF_d8, MimeType.TIFF_d16, MimeType.TIFF_d32f]:
return 'image/{}'.format(self.value)
if self is MimeType.JP2:
return 'image/jpeg2000'
if fmt in [MimeType.RAW, MimeType.REQUESTS_RESPONSE]:
return fmt.value
return mimetypes.types_map['.' + fmt.value]
if self in [MimeType.RAW, MimeType.REQUESTS_RESPONSE]:
return self.value
return mimetypes.types_map['.' + self.value]

def get_expected_max_value(self):
""" Returns max value of image `MimeType` format and raises an error if it is not an image format
Note: For `MimeType.TIFF_d32f` it will return ``1.0`` as that is expected maximum for an image even though it
could be higher.
:return: A maximum value of specified image format
:rtype: int or float
:raises: ValueError
"""
try:
return {
MimeType.TIFF: 65535,
MimeType.TIFF_d8: 255,
MimeType.TIFF_d16: 65535,
MimeType.TIFF_d32f: 1.0,
MimeType.PNG: 255,
MimeType.JPG: 255,
MimeType.JP2: 10000
}[self]
except IndexError:
raise ValueError('Type {} is not supported by this method'.format(self))


class RequestType(Enum):
Expand Down
Loading

0 comments on commit 4f648b2

Please sign in to comment.