def download_file(url, dest=None, chunk_size=1024, replace="ask",
                  label="Downloading {dest_basename} ({size:.2f}MB)",
                  expected_extension=None):
    """Download a file from a given URL and display progress.

    :param dest: If the destination exists and is a directory, the filename
        will be guessed from the Content-Disposition header. If the destination
        is an existing file, the user will either be prompted to overwrite, or
        the file will be replaced (depending on the value of **replace**). If
        the destination does not exist, it will be used as the filename.
    :param int chunk_size: bytes read in at a time.
    :param replace: If `False`, an existing destination file will not be
        overwritten.
    :param label: a string which is formatted and displayed as the progress bar
        label. Variables provided include *dest_basename*, *dest*, and *size*.
    :param expected_extension: if set, the filename will be sanitized to ensure
        it has the given extension. The extension should not start with a dot
        (`.`).
    """
    dest = Path(dest or url.split("/")[-1])
    response = get(url, stream=True)
    if (dest.exists()
            and dest.is_dir()
            and "Content-Disposition" in response.headers):
        content_disposition = rfc6266.parse_requests_response(response)
        if expected_extension is not None:
            filename = content_disposition.filename_sanitized(
                expected_extension)
        filename = secure_filename(filename)
        dest = dest / filename
    if dest.exists() and not dest.is_dir():
        if (replace is False
                or replace == "ask"
                and not click.confirm("Replace {}?".format(dest))):
            return str(dest)
    size = int(response.headers.get("content-length", 0))
    label = label.format(dest=dest, dest_basename=dest.name,
                         size=size/1024.0/1024)
    with click.open_file(str(dest), "wb") as f:
        content_iter = response.iter_content(chunk_size=chunk_size)
        with click.progressbar(content_iter, length=size/1024,
                               label=label) as bar:
            for chunk in bar:
                if chunk:
                    f.write(chunk)
                    f.flush()
    return str(dest)
def reduce_examples_3d_to_2d(example_dict, list_of_operation_dicts):
    """Reduces examples from 3-D to 2-D.

    If the examples contain both 2-D azimuthal-shear images and 3-D
    reflectivity images:

    - Keys "reflectivity_image_matrix_dbz" and "az_shear_image_matrix_s01" are
      required.
    - "radar_heights_m_agl" should contain only reflectivity heights.
    - "radar_field_names" should contain only the names of azimuthal-shear
      fields.

    If the examples contain 3-D radar images and no 2-D images:

    - Key "radar_image_matrix" is required.
    - Each field in "radar_field_names" appears at each height in
      "radar_heights_m_agl".
    - Thus, there are F_r elements in "radar_field_names", H_r elements in
      "radar_heights_m_agl", and F_r * H_r field-height pairs.

    After dimensionality reduction (from 3-D to 2-D):

    - Keys "reflectivity_image_matrix_dbz", "az_shear_image_matrix_s01", and
      "radar_heights_m_agl" will be absent.
    - Key "radar_image_matrix" will be present.  The dimensions will be
      E x M x N x C.
    - Key "radar_field_names" will be a length-C list, where the [j]th item is
      the field name for the [j]th channel of radar_image_matrix
      (radar_image_matrix[..., j]).
    - Key "min_radar_heights_m_agl" will be a length-C numpy array, where the
      [j]th item is the MINIMUM height for the [j]th channel of
      radar_image_matrix.
    - Key "max_radar_heights_m_agl" will be a length-C numpy array, where the
      [j]th item is the MAX height for the [j]th channel of radar_image_matrix.
    - Key "radar_layer_operation_names" will be a length-C list, where the [j]th
      item is the name of the operation used to create the [j]th channel of
      radar_image_matrix.

    :param example_dict: See doc for `write_example_file`.
    :param list_of_operation_dicts: See doc for `_check_layer_operation`.
    :return: example_dict: See general discussion above, for how the input
        `example_dict` is changed to the output `example_dict`.
    """

    if RADAR_IMAGE_MATRIX_KEY in example_dict:
        num_radar_dimensions = len(
            example_dict[RADAR_IMAGE_MATRIX_KEY].shape
        ) - 2

        assert num_radar_dimensions == 3

    new_radar_image_matrix = None
    new_field_names = []
    new_min_heights_m_agl = []
    new_max_heights_m_agl = []
    new_operation_names = []

    if AZ_SHEAR_IMAGE_MATRIX_KEY in example_dict:
        new_radar_image_matrix = example_dict[AZ_SHEAR_IMAGE_MATRIX_KEY] + 0.

        for this_field_name in example_dict[RADAR_FIELDS_KEY]:
            new_field_names.append(this_field_name)
            new_operation_names.append(MAX_OPERATION_NAME)

            if this_field_name == radar_utils.LOW_LEVEL_SHEAR_NAME:
                new_min_heights_m_agl.append(0)
                new_max_heights_m_agl.append(2000)
            else:
                new_min_heights_m_agl.append(3000)
                new_max_heights_m_agl.append(6000)

    for this_operation_dict in list_of_operation_dicts:
        this_new_matrix, this_operation_dict = _apply_layer_operation(
            example_dict=example_dict, operation_dict=this_operation_dict)

        this_new_matrix = numpy.expand_dims(this_new_matrix, axis=-1)

        if new_radar_image_matrix is None:
            new_radar_image_matrix = this_new_matrix + 0.
        else:
            new_radar_image_matrix = numpy.concatenate(
                (new_radar_image_matrix, this_new_matrix), axis=-1
            )

        new_field_names.append(this_operation_dict[RADAR_FIELD_KEY])
        new_min_heights_m_agl.append(this_operation_dict[MIN_HEIGHT_KEY])
        new_max_heights_m_agl.append(this_operation_dict[MAX_HEIGHT_KEY])
        new_operation_names.append(this_operation_dict[OPERATION_NAME_KEY])

    example_dict.pop(REFL_IMAGE_MATRIX_KEY, None)
    example_dict.pop(AZ_SHEAR_IMAGE_MATRIX_KEY, None)
    example_dict.pop(RADAR_HEIGHTS_KEY, None)

    example_dict[RADAR_IMAGE_MATRIX_KEY] = new_radar_image_matrix
    example_dict[RADAR_FIELDS_KEY] = new_field_names
    example_dict[MIN_RADAR_HEIGHTS_KEY] = numpy.array(
        new_min_heights_m_agl, dtype=int)
    example_dict[MAX_RADAR_HEIGHTS_KEY] = numpy.array(
        new_max_heights_m_agl, dtype=int)
    example_dict[RADAR_LAYER_OPERATION_NAMES_KEY] = new_operation_names

    return example_dict