Commit 55d65f4c authored by Daniel Scheffler's avatar Daniel Scheffler

Merge branch 'master' into import_majority_filter

parents 6495298a 9d2b1b1a
Pipeline #3338 failed with stages
in 3 minutes and 58 seconds
......@@ -20,8 +20,10 @@ test_arosics:
stage: test
script:
- source /root/miniconda3/bin/activate
- export GDAL_DATA=/root/miniconda3/share/gdal
- source activate ci_env
- export GDAL_DATA=/root/miniconda3/envs/ci_env/share/gdal
- export PYTHONPATH=$PYTHONPATH:/root # /root <- directory needed later
- pip install "py_tools_ds>=0.13.7" # FIXME remove as soon as docker runner has been updated
- make nosetests
- make docs
artifacts:
......@@ -37,8 +39,10 @@ test_styles:
stage: test
script:
- source /root/miniconda3/bin/activate
- export GDAL_DATA=/root/miniconda3/share/gdal
- source activate ci_env
- export GDAL_DATA=/root/miniconda3/envs/ci_env/share/gdal
- export PYTHONPATH=$PYTHONPATH:/root # /root <- directory needed later
# - pip install "pycodestyle>=2.0.0,!=2.4.0" # TODO remove as soon as docker runner is recreated
- make lint
artifacts:
paths:
......@@ -52,11 +56,11 @@ test_arosics_install:
stage: test
script:
- source /root/miniconda3/bin/activate
- export GDAL_DATA=/root/miniconda3/share/gdal
- conda create -y -q --name arosics python=3
- source activate arosics
- conda create -y -q --name arosics_testinstall python=3
- source activate arosics_testinstall
# resolve some requirements with conda
- conda install --yes -q -c conda-forge numpy gdal scikit-image matplotlib pyproj rasterio shapely geopandas
- conda install --yes -q -c conda-forge libgdal ncurses # Fix for libgdal installed from defaults channel causing libkea.so.1.4.7: cannot open shared object file: No such file or directory
# run installer
- python setup.py install
# test if its importable
......@@ -107,6 +111,7 @@ deploy_pypi:
- test_arosics
script: # Configure the PyPI credentials, then push the package, and cleanup the creds.
- source /root/miniconda3/bin/activate
- source activate ci_env
- mkdir -p public/images/
- cp -r docs/images/* public/images/
- printf "[distutils]\nindex-servers =\n pypi\n\n" >> ~/.pypirc
......
......@@ -4,7 +4,7 @@ import os
import time
import warnings
from copy import copy
from typing import Iterable
from typing import Iterable, Union, Tuple # noqa F401
# custom
try:
......@@ -339,11 +339,22 @@ class COREG(object):
'COREG._set_outpathes() expects two file pathes (string) or two instances of the ' \
'GeoArray class. Received %s and %s.' % (type(im_ref), type(im_tgt))
def get_baseN(path): return os.path.splitext(os.path.basename(path))[0]
def get_baseN(path):
return os.path.splitext(os.path.basename(path))[0]
# get input pathes
path_im_ref = im_ref.filePath if isinstance(im_ref, GeoArray) else im_ref
path_im_tgt = im_tgt.filePath if isinstance(im_tgt, GeoArray) else im_tgt
# get input paths
def get_input_path(im):
path = im.filePath if isinstance(im, GeoArray) else im
if isinstance(im, GeoArray) and im.filePath is None and self.path_out == 'auto':
raise ValueError(self.path_out, "The output path must be explicitly set in case the input "
"reference or target image is in-memory (without a reference to a "
"physical file on disk). Received path_out='%s'." % self.path_out)
return path
path_im_ref = get_input_path(im_ref)
path_im_tgt = get_input_path(im_tgt)
if self.path_out: # this also applies to self.path_out='auto'
......@@ -432,34 +443,17 @@ class COREG(object):
if not self.q:
print("Equalizing pixel grids and projections of reference and target image...")
# noinspection PyProtectedMember
def apply_subset_bandnames_metadata(geoArr_cr):
# TODO: replace that function with geoArr.get_subset(zslice=slice(band4match, band4match+1))
# TODO: as soon as all metadata are passed through get_subset()
zslice = slice(geoArr_cr.band4match, geoArr_cr.band4match+1)
if geoArr_cr._bandnames:
geoArr_cr.bandnames = list(np.array(list(geoArr_cr._bandnames))[zslice])
if geoArr_cr._metadata is not None:
geoArr_cr.metadata = \
geoArr_cr._metadata[list(np.array(range(len(geoArr_cr._metadata.columns)))[zslice])].copy()
return geoArr_cr
if self.grid2use == 'ref':
# resample target image to reference image
self.shift.arr = self.shift[:, :, self.shift.band4match] # resample the needed band only
self.shift = apply_subset_bandnames_metadata(self.shift)
if self.shift.bands > 1:
self.shift = self.shift.get_subset(zslice=slice(self.shift.band4match, self.shift.band4match + 1))
self.shift.reproject_to_new_grid(prototype=self.ref, CPUs=self.CPUs)
self.shift.band4match = 0 # after resampling there is only one band in the GeoArray
else:
# resample reference image to target image
# FIXME in case of different projections this will change the projection of the reference image!
self.ref.arr = self.ref[:, :, self.ref.band4match] # resample the needed band only
self.ref = apply_subset_bandnames_metadata(self.ref)
if self.ref.bands > 1:
self.ref = self.ref.get_subset(zslice=slice(self.ref.band4match, self.ref.band4match + 1))
self.ref.reproject_to_new_grid(prototype=self.shift, CPUs=self.CPUs)
self.ref.band4match = 0 # after resampling there is only one band in the GeoArray
......@@ -469,14 +463,8 @@ class COREG(object):
# TODO different colors for polygons
assert self.overlap_poly, 'Please calculate the overlap polygon first.'
try:
import folium
import geojson
except ImportError:
folium, geojson = None, None
if not folium or not geojson:
raise ImportError("This method requires the libraries 'folium' and 'geojson'. They can be installed with "
"the shell command 'pip install folium geojson'.")
import folium
import geojson
refPoly = reproject_shapelyGeometry(self.ref.poly, self.ref.epsg, 4326)
shiftPoly = reproject_shapelyGeometry(self.shift.poly, self.shift.epsg, 4326)
......@@ -610,7 +598,6 @@ class COREG(object):
PLT.subplot_3dsurface(scps.astype(np.float32))
def _get_opt_winpos_winsize(self):
# type: (tuple,tuple) -> None
"""
Calculates optimal window position and size in reference image units according to DGM, cloud_mask and
trueCornerLonLat.
......@@ -713,7 +700,8 @@ class COREG(object):
# Check, ob match Fenster größer als anderes Fenster
if not (matchBox.mapPoly.within(otherBox.mapPoly) or matchBox.mapPoly == otherBox.mapPoly):
# dann für anderes Fenster kleinstes Fenster finden, das match-Fenster umgibt
otherBox.boxImYX = get_smallest_boxImYX_that_contains_boxMapYX(matchBox.boxMapYX, otherBox.gt)
otherBox.boxImYX = get_smallest_boxImYX_that_contains_boxMapYX(
matchBox.boxMapYX, otherBox.gt, tolerance_ndigits=5) # avoids float coordinate rounding issues
# evtl. kann es sein, dass bei Shift-Fenster-Vergrößerung das shift-Fenster zu groß für den overlap wird
t_start = time.time()
......@@ -722,7 +710,8 @@ class COREG(object):
xLarger, yLarger = otherBox.is_larger_DimXY(overlapWin.boundsIm)
matchBox.buffer_imXY(-1 if xLarger else 0, -1 if yLarger else 0)
previous_area = otherBox.mapPoly.area
otherBox.boxImYX = get_smallest_boxImYX_that_contains_boxMapYX(matchBox.boxMapYX, otherBox.gt)
otherBox.boxImYX = get_smallest_boxImYX_that_contains_boxMapYX(
matchBox.boxMapYX, otherBox.gt, tolerance_ndigits=5) # avoids float coordinate rounding issues)
if previous_area == otherBox.mapPoly.area or time.time() - t_start > 1.5:
# happens e.g in case of a triangular footprint
......@@ -840,7 +829,7 @@ class COREG(object):
@staticmethod
def _shrink_winsize_to_binarySize(win_shape_YX, target_size=None):
# type: (tuple, tuple, int , int) -> any
# type: (tuple, tuple) -> Union[Tuple[int, int], None]
"""Shrinks a given window size to the closest binary window size (a power of 2) -
separately for X- and Y-dimension.
......@@ -1136,7 +1125,7 @@ class COREG(object):
def _get_grossly_deshifted_images(self, im0, im1, x_intshift, y_intshift):
# TODO this is also implemented in GeoArray # this should update ref.win.data and shift.win.data
# FIXME avoid that matching window gets smaller although shifting it with the previous win_size would not move
# FIXME it into nodata-area
# it into nodata-area
# get_grossly_deshifted_im0
old_center_YX = np.array(im0.shape) / 2
new_center_YX = [old_center_YX[0] + y_intshift, old_center_YX[1] + x_intshift]
......
......@@ -4,7 +4,6 @@ import warnings
import os
from copy import copy
from six import PY2
from importlib import util
# custom
try:
......@@ -17,7 +16,8 @@ except ImportError:
pyfftw = None
import numpy as np
from matplotlib import pyplot as plt
from mpl_toolkits.axes_grid1 import make_axes_locatable
from matplotlib.colorbar import ColorbarBase
from matplotlib.colors import Normalize
from .Tie_Point_Grid import Tie_Point_Grid
from .CoReg import COREG
......@@ -325,7 +325,7 @@ class COREG_LOCAL(object):
def CoRegPoints_table(self):
"""Returns a GeoDataFrame with the columns 'geometry','POINT_ID','X_IM','Y_IM','X_UTM','Y_UTM','X_WIN_SIZE',
'Y_WIN_SIZE','X_SHIFT_PX','Y_SHIFT_PX', 'X_SHIFT_M', 'Y_SHIFT_M', 'ABS_SHIFT' and 'ANGLE' containing all
information containing all the results frm coregistration for all points in the tie point grid.
information containing all the results from coregistration for all points in the tie point grid.
"""
return self.tiepoint_grid.CoRegPoints_table
......@@ -346,7 +346,7 @@ class COREG_LOCAL(object):
backgroundIm='tgt', hide_filtered=True, figsize=None, title='', vector_scale=1.,
savefigPath='', savefigDPI=96, showFig=True, vmin=None, vmax=None, return_map=False,
zoomable=False):
# type: (str, str, plt.cm, bool, str, bool, tuple, str, float, str, int, bool, float, float, bool) -> tuple
# type: (str, str, plt.cm, bool, str, bool, tuple, str, float, str, int, bool, float, float, bool, bool) -> ...
"""Shows a map of the calculated tie point grid with the target image as background.
:param shapes2plot: <str> 'points': plot points representing values of 'attribute2plot' onto the map
......@@ -380,11 +380,11 @@ class COREG_LOCAL(object):
fig, ax, map2show = backgroundIm.show_map(figsize=figsize, nodataVal=self.nodata[1], return_map=True,
band=self.COREG_obj.shift.band4match, zoomable=zoomable)
plt.tick_params(axis='both', which='major', labelsize=40)
ax.tick_params(axis='both', which='major', labelsize=40)
# ax.tick_params(axis='both', which='minor', labelsize=8)
# fig, ax, map2show = backgroundIm.show_map_utm(figsize=(20,20), nodataVal=self.nodata[1], return_map=True)
plt.title(title or attribute2plot)
ax.set_title(title or attribute2plot)
# transform all points of tie point grid to LonLat
outlierCols = [c for c in self.CoRegPoints_table.columns if 'OUTLIER' in c]
......@@ -400,7 +400,7 @@ class COREG_LOCAL(object):
# vmin = min(GDF[GDF[attribute2plot] != self.outFillVal][attribute2plot])
# vmax = max(GDF[GDF[attribute2plot] != self.outFillVal][attribute2plot])
# norm = mpl_normalize(vmin=vmin, vmax=vmax)
palette = cmap if cmap is not None else plt.cm.RdYlGn_r
palette = cmap if cmap is not None else plt.cm.get_cmap('RdYlGn_r')
if cmap is None and attribute2plot == 'ANGLE':
# import matplotlib.colors as mcolors
# colors1 = plt.cm.RdYlGn_r(np.linspace(0., 1, 128))
......@@ -412,7 +412,7 @@ class COREG_LOCAL(object):
# palette = plt.cm.hsv
import cmocean
palette = cmocean.cm.delta
palette = getattr(cmocean.cm, 'delta')
# GDF['color'] = [*GDF[attribute2plot].map(lambda val: palette(norm(val)))]
# add tie point grid to map
......@@ -435,20 +435,20 @@ class COREG_LOCAL(object):
if self.tieP_filter_level > 0:
# flag level 1 outliers
GDF_filt = GDF[GDF.L1_OUTLIER.__eq__(True)].copy()
plt.scatter(GDF_filt['plt_X'], GDF_filt['plt_Y'], c='b', marker=marker, s=250, alpha=1.0,
label='reliability')
ax.scatter(GDF_filt['plt_X'], GDF_filt['plt_Y'], c='b', marker=marker, s=250, alpha=1.0,
label='reliability')
if self.tieP_filter_level > 1:
# flag level 2 outliers
GDF_filt = GDF[GDF.L2_OUTLIER.__eq__(True)].copy()
plt.scatter(GDF_filt['plt_X'], GDF_filt['plt_Y'], c='r', marker=marker, s=150, alpha=1.0, label='SSIM')
ax.scatter(GDF_filt['plt_X'], GDF_filt['plt_Y'], c='r', marker=marker, s=150, alpha=1.0, label='SSIM')
if self.tieP_filter_level > 2:
# flag level 3 outliers
GDF_filt = GDF[GDF.L3_OUTLIER.__eq__(True)].copy()
plt.scatter(GDF_filt['plt_X'], GDF_filt['plt_Y'], c='y', marker=marker, s=250, alpha=1.0,
label='RANSAC')
ax.scatter(GDF_filt['plt_X'], GDF_filt['plt_Y'], c='y', marker=marker, s=250, alpha=1.0,
label='RANSAC')
if self.tieP_filter_level > 0:
plt.legend(loc=0, scatterpoints=1)
ax.legend(loc=0, scatterpoints=1)
# plot all points or vectors on top
if not GDF.empty:
......@@ -460,34 +460,30 @@ class COREG_LOCAL(object):
if shapes2plot == 'vectors':
# plot shift vectors
# doc: https://matplotlib.org/devdocs/api/_as_gen/matplotlib.axes.Axes.quiver.html
plt.quiver(GDF['plt_X'], GDF['plt_Y'],
-GDF['X_SHIFT_M'], -GDF['Y_SHIFT_M'], # invert absolute shifts to make arrows point to tgt
GDF[attribute2plot].clip(vmin, vmax), # sets the colors
scale=1200 / vector_scale, # larger values decrease the arrow length
width=.0015, # arrow width (in relation to plot width)
# linewidth=1, # maybe use this to mark outliers instead of scatter points
cmap=palette,
pivot='middle' # position the middle point of the arrows onto the tie point location
)
mappable = None
ax.quiver(GDF['plt_X'], GDF['plt_Y'],
-GDF['X_SHIFT_M'], -GDF['Y_SHIFT_M'], # invert absolute shifts to make arrows point to tgt
GDF[attribute2plot].clip(vmin, vmax), # sets the colors
scale=1200 / vector_scale, # larger values decrease the arrow length
width=.0015, # arrow width (in relation to plot width)
# linewidth=1, # maybe use this to mark outliers instead of scatter points
cmap=palette,
pivot='middle' # position the middle point of the arrows onto the tie point location
)
elif shapes2plot == 'points':
# plot tie points
points = plt.scatter(GDF['plt_X'], GDF['plt_Y'], c=GDF[attribute2plot], lw=0,
cmap=palette, marker='o' if len(GDF) < 10000 else '.', s=50, alpha=1.0,
vmin=vmin, vmax=vmax)
mappable = points
ax.scatter(GDF['plt_X'], GDF['plt_Y'], c=GDF[attribute2plot], lw=0,
cmap=palette, marker='o' if len(GDF) < 10000 else '.', s=50, alpha=1.0,
vmin=vmin, vmax=vmax)
else:
raise ValueError("The parameter 'shapes2plot' must be set to 'vectors' or 'points'. Received %s."
% shapes2plot)
# add colorbar
divider = make_axes_locatable(plt.gca())
# create axis on the right; size =2% of ax; padding = 0.1 inch
cax = divider.append_axes("right", size="2%", pad=0.1)
plt.colorbar(mappable, cax=cax)
p = ax.get_position().get_points().flatten() # [left, bottom, right, top]
cax = fig.add_axes([p[2] + 0.02, p[1], 0.02, p[3] - p[1]]) # [left, bottom, width, height]
ColorbarBase(cax, cmap=palette, norm=Normalize(vmin=vmin, vmax=vmax), orientation='vertical')
else:
if not self.q:
......@@ -508,13 +504,9 @@ class COREG_LOCAL(object):
warnings.warn(UserWarning('This function is still under construction and may not work as expected!'))
assert self.CoRegPoints_table is not None, 'Calculate tie point grid first!'
if not all([util.find_spec('folium'), util.find_spec('geojson')]):
raise ImportError("This method requires the libraries 'folium' and 'geojson'. They can be installed with "
"the shell command 'pip install folium geojson'.")
import folium
import geojson
from folium import plugins
from folium.raster_layers import ImageOverlay
lon_min, lat_min, lon_max, lat_max = \
reproject_shapelyGeometry(self.im2shift.box.mapPoly, self.im2shift.projection, 4326).bounds
......@@ -532,7 +524,7 @@ class COREG_LOCAL(object):
# create map
map_osm = folium.Map(location=[center_lat, center_lon]) # ,zoom_start=3)
# import matplotlib
plugins.ImageOverlay(
ImageOverlay(
colormap=lambda x: (1, 0, 0, x), # TODO a colormap must be given
# colormap=matplotlib.cm.gray, # does not work
image=image2plot, bounds=[[lat_min, lon_min], [lat_max, lon_max]],
......
......@@ -3,6 +3,7 @@
import collections
import time
import warnings
import numpy as np
# internal modules
from geoarray import GeoArray
......@@ -246,10 +247,11 @@ class DESHIFTER(object):
# snap clipextent to output grid
# (in case of odd input coords the output coords are moved INSIDE the input array)
xmin, ymin, xmax, ymax = self.clipextent
xmin = find_nearest(self.out_grid[0], xmin, roundAlg='on', extrapolate=True)
ymin = find_nearest(self.out_grid[1], ymin, roundAlg='on', extrapolate=True)
xmax = find_nearest(self.out_grid[0], xmax, roundAlg='off', extrapolate=True)
ymax = find_nearest(self.out_grid[1], ymax, roundAlg='off', extrapolate=True)
x_tol, y_tol = float(np.ptp(self.out_grid[0]) / 10000), float(np.ptp(self.out_grid[1]) / 10000) # 10.000th pix
xmin = find_nearest(self.out_grid[0], xmin, roundAlg='on', extrapolate=True, tolerance=x_tol)
ymin = find_nearest(self.out_grid[1], ymin, roundAlg='on', extrapolate=True, tolerance=y_tol)
xmax = find_nearest(self.out_grid[0], xmax, roundAlg='off', extrapolate=True, tolerance=x_tol)
ymax = find_nearest(self.out_grid[1], ymax, roundAlg='off', extrapolate=True, tolerance=y_tol)
return xmin, ymin, xmax, ymax
def correct_shifts(self):
......
......@@ -381,7 +381,8 @@ class Tie_Point_Grid(object):
'Y_SHIFT_M', 'ABS_SHIFT', 'ANGLE', 'SSIM_BEFORE', 'SSIM_AFTER',
'SSIM_IMPROVED', 'RELIABILITY', 'LAST_ERR'])
GDF = GDF.merge(records, on='POINT_ID', how="inner")
# merge DataFrames (dtype must be equal to records.dtypes; We need np.object due to None values)
GDF = GDF.astype(np.object).merge(records.astype(np.object), on='POINT_ID', how="inner")
GDF = GDF.fillna(int(self.outFillVal))
if not self.q:
......@@ -700,11 +701,16 @@ class Tie_Point_Grid(object):
"""
GDF = self.CoRegPoints_table
GDF2pass = GDF if not skip_nodata else GDF[GDF[skip_nodata_col] != self.outFillVal]
if skip_nodata:
GDF2pass = GDF[GDF[skip_nodata_col] != self.outFillVal].copy()
else:
GDF2pass = GDF
GDF2pass.LAST_ERR = GDF2pass.apply(lambda GDF_row: repr(GDF_row.LAST_ERR), axis=1)
# replace boolean values (cannot be written)
GDF2pass = GDF2pass.replace(False, 0) # replace all booleans where column dtype is not np.bool but np.object
GDF2pass = GDF2pass.replace(True, 1)
GDF2pass = GDF2pass.replace(False, 0).copy() # replace booleans where column dtype is not np.bool but np.object
GDF2pass = GDF2pass.replace(True, 1).copy()
for col in GDF2pass.columns:
if GDF2pass[col].dtype == np.bool:
GDF2pass[col] = GDF2pass[col].astype(int)
......
......@@ -10,10 +10,11 @@ from arosics.CoReg_local import COREG_LOCAL
from arosics.DeShifter import DESHIFTER
from arosics.Tie_Point_Grid import Tie_Point_Grid
from .version import __version__, __versionalias__ # noqa (E402 + F401)
__author__ = """Daniel Scheffler"""
__email__ = 'daniel.scheffler@gfz-potsdam.de'
__version__ = '0.8.6'
__versionalias__ = '2018-07-20_01'
__all__ = ['COREG',
'COREG_LOCAL',
'DESHIFTER',
......
__version__ = '0.8.13'
__versionalias__ = '2018-12-04_01'
geoarray>=0.6.16
py_tools_ds>=0.12.1
geoarray>=0.8.0
py_tools_ds>=0.13.7
cmocean
numpy
gdal
......@@ -9,3 +9,5 @@ matplotlib
geopandas
plotly
six
folium>=0.6.0
geojson
......@@ -8,4 +8,3 @@ coverage==4.1
Sphinx==1.4.8
cryptography==1.7
PyYAML==3.11
cmocean
plotly
six
folium>=0.6.0
geojson
......@@ -13,22 +13,26 @@ with open('README.rst') as readme_file:
with open('HISTORY.rst') as history_file:
history = history_file.read()
requirements = ['numpy', 'gdal', 'shapely', 'scikit-image', 'matplotlib', 'geopandas', 'geoarray>=0.6.16',
'py_tools_ds>=0.12.1', 'plotly', 'cmocean', 'six',
version = {}
with open("arosics/version.py") as version_file:
exec(version_file.read(), version)
requirements = ['numpy', 'gdal', 'shapely', 'scikit-image', 'matplotlib', 'geopandas', 'geoarray>=0.8.0',
'py_tools_ds>=0.13.7', 'plotly', 'cmocean', 'six', 'folium>=0.6.0', 'geojson'
# 'pykrige' # conda install --yes -c conda-forge pykrige
# 'pyfftw', # conda install --yes -c conda-forge pyfftw=0.10.4 ; \
# 'basemap', # conda install --yes -c conda-forge basemap; \
]
setup_requirements = [
# TODO(danschef): put setup requirements (distutils extensions, etc.) here
'setuptools'
]
test_requirements = requirements + ['coverage', 'nose', 'nose-htmloutput', 'rednose']
setup(
name='arosics',
version='0.8.6',
version=version['__version__'],
description="An Automated and Robust Open-Source Image Co-Registration Software for Multi-Sensor Satellite Data",
long_description=readme + '\n\n' + history,
author="Daniel Scheffler",
......@@ -53,6 +57,7 @@ setup(
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
],
test_suite='tests',
tests_require=test_requirements,
......
......@@ -2,7 +2,7 @@
context_dir="./context"
dockerfile="arosics_ci.docker"
tag="arosics_ci:0.8.6"
tag="arosics_ci:0.8.7"
gitlab_runner="arosics_gitlab_CI_runner"
echo "#### Build runner docker image"
......
FROM gms_base_centos:latest
FROM gms_base_centos:0.2
# copy some needed stuff to /root
COPY *.yml /root/
# update the ci_env environment (that already contains all packages installed via 'docker_pyenvs' repo)
RUN /bin/bash -i -c "\
source /root/miniconda3/bin/activate ; \
conda env update -n root -f /root/environment_arosics.yml"
source activate ci_env; \
conda env update -n ci_env -f /root/environment_arosics.yml"
......@@ -9,6 +9,7 @@ dependencies:
- pyqt
- numpy
- gdal
- conda-forge::libgdal # force to use conda-forge for libgdal to avoid package version incompatiblies due to mixed channels
- scikit-image
- rasterio
- pyproj
......@@ -33,8 +34,10 @@ dependencies:
- sphinx-argparse
- six
- spectral
- folium>=0.6.0
- geojson
- flake8
- pycodestyle
- pycodestyle<2.4.0 # fixes ImportError: module 'pycodestyle' has no attribute 'break_around_binary_operator'
- pylint
- pydocstyle
- nose
......@@ -44,5 +47,5 @@ dependencies:
- rednose
- cmocean
- plotly
- geoarray>=0.6.16
- py_tools_ds>=0.12.1
- geoarray>=0.8.0
- py_tools_ds>=0.13.7
......@@ -21,7 +21,7 @@ test_cases = dict(
footprint_poly_tgt='POLYGON ((341890 5866490, 356180 5866490, 356180 5834970, 335440 5834970, '
'335490 5845270, 341890 5866490))',
progress=False,
v=0),
v=False),
wp_inside=(344720, 5848485), # inside of overlap
wp_covering_nodata=(339611, 5856426), # close to the image edge of the input images -> win>64px covers nodata
wp_close_to_edge=(353810, 5840516), # close to the image edge of the input images -> win>64px covers nodata
......
......@@ -6,6 +6,7 @@
import unittest
import shutil
import os
import numpy as np
# custom
from .cases import test_cases
......@@ -102,6 +103,38 @@ class CompleteWorkflow_INTER1_S2A_S2A(unittest.TestCase):
footprint_poly_tgt=None))
self.assertTrue(CR.success)
def test_shift_calculation_with_float_coords(self):
"""Test with default parameters - should compute X/Y shifts properly ad write the de-shifted target image."""
# overwrite gt and prj
ref = GeoArray(self.ref_path)
ref.to_mem()
ref.filePath = None
ref.gt = [330000.00000001, 10.1, 0.0, 5862000.0000001, 0.0, -10.1]
tgt = GeoArray(self.tgt_path)
tgt.to_mem()
tgt.filePath = None
tgt.gt = [335440.0000001, 10.1, 0.0, 5866490.0000001, 0.0, -10.1]
CR = self.run_shift_detection_correction(ref, tgt,
**dict(self.coreg_kwargs,
wp=(341500.0, 5861440.0),
footprint_poly_ref=None,
footprint_poly_tgt=None))
self.assertTrue(CR.success)
def test_shift_calculation_inmem_gAs_path_out_auto(self):
"""Test input parameter path_out='auto' in case input reference/ target image are in-memory GeoArrays."""
ref = GeoArray(np.random.randint(1, 100, (1000, 1000)))
tgt = GeoArray(np.random.randint(1, 100, (1000, 1000)))
with self.assertRaises(ValueError):
self.run_shift_detection_correction(ref, tgt,
**dict(self.coreg_kwargs,
path_out='auto',
fmt_out='ENVI',
v=True))
# @unittest.SkipTest
def test_shift_calculation_verboseMode(self):
"""Test the verbose mode - runs the functions of the plotting submodule."""
......
......@@ -78,6 +78,28 @@ class CompleteWorkflow_INTER1_S2A_S2A(unittest.TestCase):
self.assertTrue(os.path.exists(self.coreg_kwargs['path_out']),
'Output of local co-registration has not been written.')
def test_calculation_of_tie_point_grid_float_coords(self):
# NOTE: This does not test against unequaly sized output of get_image_windows_to_match().
# overwrite gt and prj
ref = GeoArray(self.ref_path)
ref.to_mem()
ref.filePath = None
ref.gt = [330000.19999996503, 0.6, 0.0, 5862000.7999997628, 0.0, -0.6]
# ref.gt = [330000.1, 10.1, 0.0, 5862000.1, 0.0, -10.1]
tgt = GeoArray(self.tgt_path)
tgt.to_mem()
tgt.filePath = None
tgt.gt = [330000.19999996503, 0.6, 0.0, 5862000.7999997628, 0.0, -0.6]
# get instance of COREG_LOCAL object
CRL = COREG_LOCAL(ref, tgt, **dict(**self.coreg_kwargs))
# use the getter of the CoRegPoints_table to calculate tie point grid
# noinspection PyStatementEffect
CRL.CoRegPoints_table
# if __name__ == '__main__':
# unittest.main(argv=['first-arg-is-ignored'],exit=False, verbosity=2)
#
......
......@@ -74,6 +74,11 @@ class Test_Tie_Point_Grid(unittest.TestCase):
self.TPG.to_PointShapefile(outpath)
self.assertTrue(os.path.isfile(outpath))
with tempfile.TemporaryDirectory() as tmpdir:
outpath = os.path.join(tmpdir, 'test_out_shapefile_incl_nodata.shp')
self.TPG.to_PointShapefile(outpath, skip_nodata=False)
self.assertTrue(os.path.isfile(outpath))
def test_to_vectorfield(self):