docs: def make_three_color(data, time, step, config, shape=(1280, 1280), lower_val=(0, 0, 0), upper_val=(2.5, 2.5, 2.5)):
      """
      create a three color image according to the config file
      :param data: a dictionary of fetched data where keys correspond to products
      :param config: a config object
      :param shape: the size of a composite image
      :param lower_val: a tuple of lower values for RGB, any value below this is set to the low value
      :param upper_val: a tuple of upper values for RGB, any value above this is set to the high value
      :return: a (m,n,3) numpy array for a three color image where all values are between 0 and 1
      """
      order = {'red': 0, 'green': 1, 'blue': 2}
  
      three_color = np.zeros((shape[0], shape[1], 3))
      channel_colors = {color: config.default[color] for color in ['red', 'green', 'blue']}
  
      for color, channel in channel_colors.items():
          if data[channel][1] is None or \
                  abs((time - date_parser.parse(data[channel][0]['date-end'])).total_seconds()) > step.total_seconds()/2.0:
              return np.zeros((shape[0], shape[1], 3))
  
          three_color[:, :, order[color]] = data[channel][1]
  
          # scale the image by the power
          three_color[:, :, order[color]] = np.power(three_color[:, :, order[color]],
                                                     config.default["{}_power".format(color)])
  
          # adjust the percentile thresholds
          lower = lower_val[order[color]]
          upper = upper_val[order[color]]
          three_color[np.where(three_color[:, :, order[color]] < lower)] = lower
          three_color[np.where(three_color[:, :, order[color]] > upper)] = upper
  
      # image values must be between (0,1) so scale image
      for color, index in order.items():
          three_color[:, :, index] /= upper_val[order[color]]
      return three_color