docs: def geometric_transform(data, mapping = "c0,c1", output_shape=None,
                          mode='constant', interpolation="linear"):
      """
      Apply an arbitrary geometric transform.
      The given mapping function is used to find, for each point in the
      output, the corresponding coordinates in the input. The value of the
      input at those coordinates is determined by spline interpolation of
      the requested order.
      Parameters
      ----------
      %(input)s
      mapping : {callable, scipy.LowLevelCallable}
          A callable object that accepts a tuple of length equal to the output
          array rank, and returns the corresponding input coordinates as a tuple
          of length equal to the input array rank.
      """
  
      if not (isinstance(data, np.ndarray) and data.ndim in (2, 3)):
          raise ValueError("input data has to be a 2d or 3d array!")
  
      interpolation_defines = {"linear": ["-D", "SAMPLER_FILTER=CLK_FILTER_LINEAR"],
                               "nearest": ["-D", "SAMPLER_FILTER=CLK_FILTER_NEAREST"]}
  
      mode_defines = {"constant": ["-D", "SAMPLER_ADDRESS=CLK_ADDRESS_CLAMP"],
                      "wrap": ["-D", "SAMPLER_ADDRESS=CLK_ADDRESS_REPEAT"],
                      "edge": ["-D", "SAMPLER_ADDRESS=CLK_ADDRESS_CLAMP_TO_EDGE"]
                      }
  
      if not interpolation in interpolation_defines:
          raise KeyError(
              "interpolation = '%s' not defined ,valid: %s" % (interpolation, list(interpolation_defines.keys())))
  
      if not mode in mode_defines:
          raise KeyError("mode = '%s' not defined ,valid: %s" % (mode, list(mode_defines.keys())))
  
      if not data.dtype.type in cl_buffer_datatype_dict:
          raise KeyError("dtype %s not supported yet (%s)" % (data.dtype.type, tuple(cl_buffer_datatype_dict.keys())))
  
      dtype_defines = ["-D", "DTYPE={type}".format(type=cl_buffer_datatype_dict[data.dtype.type])]
  
      image_functions = {np.float32:"read_imagef",
                         np.uint8: "read_imageui",
                         np.uint16: "read_imageui",
                         np.int32: "read_imagei"}
  
      image_read_defines = ["-D","READ_IMAGE=%s"%image_functions[data.dtype.type]]
  
      with open(abspath("kernels/geometric_transform.cl"), "r") as f:
          tpl = Template(f.read())
  
      output_shape = tuple(output_shape)
  
      mappings = {"FUNC2": "c1,c0",
                  "FUNC3": "c2,c1,c0"}
  
      mappings["FUNC%d" % data.ndim] = ",".join(reversed(mapping.split(",")))
  
      rendered = tpl.render(**mappings)
  
      d_im = OCLImage.from_array(data)
      res_g = OCLArray.empty(output_shape, data.dtype)
  
      prog = OCLProgram(src_str=rendered,
                        build_options=interpolation_defines[interpolation] +
                                      mode_defines[mode] + dtype_defines+image_read_defines)
  
      kernel = "geometric_transform{ndim}".format(ndim=data.ndim)
  
      prog.run_kernel(kernel,
                      output_shape[::-1], None,
                      d_im, res_g.data)
  
      return res_g.get()