text: #!/usr/bin/python
  
  import logging
  import shapely.wkt
  import pyproj
  import xarray
  from shapely.ops import transform
  import uuid
  from pathlib import Path
  import numpy
  
  # --------------------------------------------------------------------------------------
  # Save this code in file "process_wrapper.py" and adapt as indicated in inline comments.
  #
  # Notes:
  #  - This is a Python 3 script.
  #  - The inputs will be given values by name, thus their order has no importance ...
  #  - ... except that the inputs with a default value must be listed last.
  #  - Parameter names are automatically converted into valid Python variable names.
  #  - Any empty line or line starting with a '#' character will be ignored.
  # --------------------------------------------------------------------------------------
  
  
  logger = logging.getLogger(__name__)
  logging.basicConfig(level=logging.INFO)
  
  def execute(out_dir, wkt, product):
      """
      Inputs:
      wkt -- wkt -- 92/WKT String
      product -- product -- 45/User String
  
      Outputs:
      result -- result -- 45/User String
  
      Main Dependency:
      mep-wps/uc-bundle-1
  
      Software Dependencies:
      pywps-4
  
      Processing Resources:
      ram -- 4
      disk -- 10
      cpu -- 1
      gpu -- 0
      """
  
      # ----------------------------------------------------------------------------------
      # Insert your own code below.
      # The files generated by your code must be stored in the "out_dir" folder.
      # Only the content of that folder is persisted in the datastore.
      # Give appropriate values to the output parameters. These will be passed to the next
      # process(es) following the workflow connections.
      # ----------------------------------------------------------------------------------
  
      logger.info("Starting...")
  
      # extract file names of the required bands
      pl=product.split("+")
      prodB4=pl[0]+[i for i in pl[1:] if "B04" in pl[0]+i][0]
      prodB8=pl[0]+[i for i in pl[1:] if "B08" in pl[0]+i][0]
      logger.info("Band B04: "+prodB4)
      logger.info("Band B08: "+prodB8)
          
      # open the files
      B4i=xarray.open_rasterio(prodB4)
      B8i=xarray.open_rasterio(prodB8)
      
      # convert area of interest into the desired coordinate system
      geom=shapely.wkt.loads(wkt)
      wgs84 = pyproj.CRS('EPSG:4326')
      gproj = pyproj.Transformer.from_crs(wgs84, B4i.crs, always_xy=True).transform
      geom = transform(gproj, geom)
      #geom=shapely.affinity.translate(geom, 120000.)
      bbox=geom.bounds
      
      # select part inside bbox
      B4=B4i.where((B4i.x>=bbox[0]) & (B4i.x<=bbox[2]) & (B4i.y>=bbox[1]) & (B4i.y<=bbox[3]),drop=True)
      B8=B8i.where((B8i.x>=bbox[0]) & (B8i.x<=bbox[2]) & (B8i.y>=bbox[1]) & (B8i.y<=bbox[3]),drop=True)
  
      # convert to floating point
      B4=B4.astype(numpy.float64)
      B8=B8.astype(numpy.float64)
      for indv in B4i.attrs.get('nodatavals',[]):
          B4=B4.where(B4!=indv, drop=True)
      for indv in B8i.attrs.get('nodatavals',[]):
          B8=B8.where(B8!=indv, drop=True)
  
      if B4.size>0 and B8.size>0:
  
          # compute NDVI
          NDVI=(B8-B4)/(B8+B4)
      
          # cosmetics being able to save to netcdf
          NDVI.attrs['crs']=B4i.attrs['crs']
          NDVI=NDVI.assign_coords({'band':["NDVI"]})
  
          # info
          logger.info("Shape:   "+str(NDVI.shape));
          logger.info("X-range: "+str(NDVI.x.min().values)+" ... "+str(NDVI.x.max().values));
          logger.info("Y-range: "+str(NDVI.y.min().values)+" ... "+str(NDVI.y.max().values));
      
          # Save to file
          outFile=str(Path(out_dir,"part_"+uuid.uuid4().hex))
      #     with open(outFile,'w') as f:
      #         json.dump(NDVI.to_dict(),f,default=str)
          result=NDVI.to_dataset('band')
          result.to_netcdf(outFile, engine='h5netcdf')
          
          logger.info("Saved to: "+outFile)
      
      else:
  
          outFile="<EMPTY>"
          logger.info("This part is empty, skipping.")
          
  
      logger.info("Finished...")
  
      # ----------------------------------------------------------------------------------
      # The wrapper must return a dictionary that contains the output parameter values.
      # ----------------------------------------------------------------------------------
      return {
          "result": outFile
      }