Esempio n. 1
0
def check_http_accessibility( image_base, file_extension ):
    """ Returns true if data below this image base can be accessed through HTTP.
    """
    slice_zero_url = urljoin(image_base, "0")
    first_file_url = urljoin(slice_zero_url, "0_0_0." + file_extension)
    code = urllib.urlopen(first_file_url).getcode()
    return code == 200
Esempio n. 2
0
def export_skeleton_as_nrrd_async(skeleton_id,
                                  source_ref,
                                  target_ref,
                                  user_id,
                                  mirror=True,
                                  create_message=True):

    result = export_skeleton_as_nrrd(skeleton_id, source_ref, target_ref,
                                     user_id, mirror)
    if create_message:
        msg = Message()
        msg.user = User.objects.get(pk=int(user_id))
        msg.read = False
        if result['errors']:
            msg.title = "No NRRD file could be creaed for skeleton {}".format(
                skeleton_id)
            msg.text = "There was at least one error during the NRRD export: {}".format(
                '\n'.join(result['errors']))
            msg.action = ""
        else:
            url = urljoin(
                urljoin(settings.MEDIA_URL,
                        settings.MEDIA_EXPORT_SUBDIRECTORY),
                result['nrrd_name'])
            msg.title = "Exported skeleton {} as NRRD file".format(skeleton_id)
            msg.text = "The requested skeleton was exported as NRRD file. You " \
                    "can download it from this location: <a href='{}'>{}</a>".format(url, url)
            msg.action = url
        msg.save()

    return "Errors: {}".format('\n'.join(
        result['errors'])) if result['errors'] else result['nrrd_path']
Esempio n. 3
0
def check_http_accessibility(image_base, file_extension):
    """ Returns true if data below this image base can be accessed through HTTP.
    """
    slice_zero_url = urljoin(image_base, "0")
    first_file_url = urljoin(slice_zero_url, "0_0_0." + file_extension)
    try:
        code = urllib.urlopen(first_file_url).getcode()
    except IOError:
        return False
    return code == 200
Esempio n. 4
0
def find_project_folders(image_base, path, filter_term, only_unknown, depth=1):
    """ Finds projects in a folder structure by testing for the presence of an
    info/project YAML file.
    """
    dirs = []
    projects = {}
    not_readable = []
    for current_file in glob.glob( os.path.join(path, filter_term) ):
        if os.path.isdir(current_file):
            # Check if the folder has a info file
            info_file = os.path.join(current_file, info_file_name)
            if os.path.exists(info_file):
                short_name = current_file.replace(settings.CATMAID_IMPORT_PATH, "")
                # Check if this folder is already known by testing if
                # there is a stack with the same image base as a subfolder
                # would have.
                try:
                    # Make sure we have slashes in our directery URL part
                    url_dir = short_name
                    if os.sep != '/':
                        url_dir = url_dir.replace("\\", "/")
                    project_url = urljoin(image_base, url_dir)
                    project = PreProject( info_file, project_url, short_name, only_unknown )
                    if only_unknown and project.already_known:
                        continue
                    else:
                        projects[current_file] = project
                        # Remember this project if it isn't available yet
                        dirs.append( (current_file, short_name) )
                except Exception as e:
                    not_readable.append( (info_file, e) )
            elif depth > 1:
                # Recurse in subdir if requested
                dirs = dirs + find_files( current_file, depth - 1)
    return (dirs, projects, not_readable)
Esempio n. 5
0
def get_roi_image(request, project_id=None, roi_id=None):
    """ Returns the URL to the cropped image, described by the ROI.  These
    images are cached, and won't get removed automatically. If the image is
    already present its URL is used and returned. For performance reasons it
    might be a good idea, to add this test to the web-server config.
    """
    file_name, file_path = create_roi_path(roi_id)
    if not os.path.exists(file_path):
        # Start async processing
        create_roi_image(request.user, project_id, roi_id, file_path)
        # Use waiting image
        url = urljoin(settings.STATIC_URL, "images/wait_bgwhite.gif")
    else:
        # Create real image di
        url_base = urljoin(settings.MEDIA_URL, settings.MEDIA_ROI_SUBDIRECTORY)
        url = urljoin(url_base, file_name)

    return redirect(url)
Esempio n. 6
0
File: roi.py Progetto: tomka/CATMAID
def get_roi_image(request, project_id=None, roi_id=None):
    """ Returns the URL to the cropped image, described by the ROI.  These
    images are cached, and won't get removed automatically. If the image is
    already present its URL is used and returned. For performance reasons it
    might be a good idea, to add this test to the web-server config.
    """
    file_name, file_path = create_roi_path(roi_id)
    if not os.path.exists(file_path):
        # Start async processing
        create_roi_image(request.user, project_id, roi_id, file_path)
        # Use waiting image
        url = urljoin(settings.STATIC_URL,
            "images/wait_bgwhite.gif")
    else:
        # Create real image di
        url_base = urljoin(settings.MEDIA_URL,
            settings.MEDIA_ROI_SUBDIRECTORY)
        url = urljoin(url_base, file_name)

    return redirect(url)
Esempio n. 7
0
File: nat.py Progetto: tomka/CATMAID
def export_skeleton_as_nrrd_async(skeleton_id, source_ref, target_ref, user_id,
                                  mirror=True, create_message=True):

    result = export_skeleton_as_nrrd(skeleton_id, source_ref, target_ref,
                                     user_id, mirror)
    if create_message:
        msg = Message()
        msg.user = User.objects.get(pk=int(user_id))
        msg.read = False
        if result['errors']:
            msg.title = "No NRRD file could be creaed for skeleton {}".format(skeleton_id)
            msg.text = "There was at least one error during the NRRD export: {}".format('\n'.join(result['errors']))
            msg.action = ""
        else:
            url = urljoin(urljoin(settings.MEDIA_URL, settings.MEDIA_EXPORT_SUBDIRECTORY), result['nrrd_name'])
            msg.title = "Exported skeleton {} as NRRD file".format(skeleton_id)
            msg.text = "The requested skeleton was exported as NRRD file. You " \
                    "can download it from this location: <a href='{}'>{}</a>".format(url, url)
            msg.action = url
        msg.save()

    return "Errors: {}".format('\n'.join(result['errors'])) if result['errors'] else result['nrrd_path']
Esempio n. 8
0
    def set_image_fields(self, info_object, project_url, data_folder, needs_zoom):
        """ Sets the image_base, num_zoom_levels, file_extension fields
        of the calling object. Favor a URL field, if there is one. A URL
        field, however, also requires the existence of the
        'fileextension' and and 'zoomlevels' field.
        """
        if 'url' in info_object:
            self.image_base = info_object['url']
            if needs_zoom:
                self.num_zoom_levels = info_object['zoomlevels']
            self.file_extension = info_object['fileextension']
        else:
            # The image base of a stack is the combination of the
            # project URL and the stack's folder name.
            folder = info_object['folder']
            self.image_base = urljoin(project_url, folder)
            # Favor 'zoomlevel' and 'fileextension' fields, if
            # available, but try to find this information if thosa
            # fields are not present.
            zoom_available = 'zoomlevels' in info_object
            ext_available = 'fileextension' in info_object
            if zoom_available and needs_zoom:
                self.num_zoom_levels = info_object['zoomlevels']
            if ext_available:
                self.file_extension = info_object['fileextension']
            # Only retrieve file extension and zoom level if one of
            # them is not available in the stack definition.
            if (not zoom_available and needs_zoom) or not ext_available:
                file_ext, zoom_levels = find_zoom_levels_and_file_ext(
                    data_folder, folder, needs_zoom )
                # If there is no zoom level provided, use the found one
                if not zoom_available and needs_zoom:
                    self.num_zoom_levels = zoom_levels
                # If there is no file extension level provided, use the
                # found one
                if not ext_available:
                    self.file_extension = file_ext
        # Make sure the image base has a trailing slash, because this is expected
        if self.image_base[-1] != '/':
            self.image_base = self.image_base + '/'

        # Test if the data is accessible through HTTP
        self.accessible = check_http_accessibility( self.image_base, self.file_extension )
Esempio n. 9
0
def find_project_folders(image_base, path, filter_term, only_unknown, depth=1):
    """ Finds projects in a folder structure by testing for the presence of an
    info/project YAML file.
    """
    dirs = []
    projects = {}
    not_readable = []
    for current_file in glob.glob(os.path.join(path, filter_term)):
        if os.path.isdir(current_file):
            # Check if the folder has a info file
            info_file = os.path.join(current_file, info_file_name)
            if os.path.exists(info_file):
                short_name = current_file.replace(settings.CATMAID_IMPORT_PATH,
                                                  "")
                # Check if this folder is already known by testing if
                # there is a stack with the same image base as a subfolder
                # would have.
                try:
                    # Make sure we have slashes in our directery URL part
                    url_dir = short_name
                    if os.sep != '/':
                        url_dir = url_dir.replace("\\", "/")
                    project_url = urljoin(image_base, url_dir)
                    project = PreProject(info_file, project_url, short_name,
                                         only_unknown)
                    if only_unknown and project.already_known:
                        continue
                    else:
                        projects[current_file] = project
                        # Remember this project if it isn't available yet
                        dirs.append((current_file, short_name))
                except Exception as e:
                    not_readable.append((info_file, e))
            elif depth > 1:
                # Recurse in subdir if requested
                dirs = dirs + find_files(current_file, depth - 1)
    return (dirs, projects, not_readable)
Esempio n. 10
0
    def test_import_projects(self):
        """Import a set of new projects, stacks and stack groups. This tests
        only the actual import. Retrieving the data to import from different
        sources is not part of this test.
        """
        project_url = 'https://catmaid-test/'
        data_folder = '/tmp/catmaid-test/'
        existing_projects = list(Project.objects.all())
        existing_project_ids = [p.id for p in existing_projects]

        p1_config = {
            'project': {
                'title': 'test-no-stacks',
            }
        }

        p2_config = {
            'project': {
                'title':
                'test-two-stacks',
                'stacks': [
                    {
                        # A basic stack, only with required information
                        'title':
                        'test-stack-1',
                        'dimension':
                        '(7, 17, 23)',
                        'resolution':
                        '(2, 3, 5)',
                        'zoomlevels':
                        -1,
                        'mirrors': [{
                            'title': 'test-mirror-1',
                            'fileextension': 'jpg'
                        }]
                    },
                    {
                        # A basic stack with a little more information
                        'title':
                        'test-stack-2',
                        'dimension':
                        '(7, 17, 23)',
                        'resolution':
                        '(2, 3, 5)',
                        'zoomlevels':
                        -1,
                        'mirrors': [{
                            'title': 'test-mirror-2',
                            'fileextension': 'jpg',
                            'url': 'https://this.is.my.stack/'
                        }]
                    },
                    {
                        # A stack with all optional properties
                        'title':
                        'test-stack-3',
                        'dimension':
                        '(4, 34, 9)',
                        'resolution':
                        '(1, 2, 3)',
                        'metadata':
                        'Test meta data',
                        'zoomlevels':
                        -1,
                        'translation':
                        '(10, 20, 30)',
                        'mirrors': [{
                            'title': 'test-mirror-3',
                            'folder': 'abc/',
                            'fileextension': 'jpg',
                            'tile_width': 123,
                            'tile_height': 456,
                            'tile_source_type': 2,
                        }],
                        'stackgroups': [{
                            # Add a single stack group with only this stack
                            # in it.
                            'title': 'Test group 1',
                            'relation': 'view',
                        }],
                    }
                ]
            }
        }

        pre_projects = [
            importer.PreProject(p1_config, project_url, data_folder),
            importer.PreProject(p2_config, project_url, data_folder),
        ]

        tags = []
        permissions = []
        default_tile_width = 256
        default_tile_height = 512
        default_tile_source_type = 5
        default_position = 0
        cls_graph_ids_to_link = []
        remove_unref_stack_data = False

        imported, not_imported = importer.import_projects(
            self.user, pre_projects, tags, permissions, default_tile_width,
            default_tile_height, default_tile_source_type,
            cls_graph_ids_to_link, remove_unref_stack_data)

        self.assertListEqual(pre_projects, imported)
        self.assertListEqual([], not_imported)

        new_projects = list(
            Project.objects.exclude(
                id__in=existing_project_ids).order_by('title'))
        self.assertEqual(2, len(new_projects))

        # Projects should be ordered by name, so the first project will be based
        # on p1_config. Test p1 first, it is not expected to have any stacks.
        p1 = new_projects[0]
        self.assertEqual(p1_config['project']['title'], p1.title)
        self.assertEqual(0, p1.stacks.all().count())

        # Test p2.
        p2 = new_projects[1]
        self.assertEqual(p2_config['project']['title'], p2.title)
        p2_stacks = p2.stacks.all().order_by('title')
        self.assertEqual(3, len(p2_stacks))
        p2cfg_stacks = p2_config['project']['stacks']
        for n, p2s in enumerate(p2_stacks):
            stack = p2cfg_stacks[n]

            # Test required fields
            self.assertEqual(stack['title'], p2s.title)
            six.assertCountEqual(self, literal_eval(stack['dimension']),
                                 literal_eval(str(p2s.dimension)))
            six.assertCountEqual(self, literal_eval(stack['resolution']),
                                 literal_eval(str(p2s.resolution)))
            self.assertEqual(stack['zoomlevels'], p2s.num_zoom_levels)

            # Test mirrors
            mirrors = p2s.stackmirror_set.all().order_by('title')
            self.assertEqual(len(stack['mirrors']), len(mirrors))
            for m, omirror in enumerate(mirrors):
                mirror = stack['mirrors'][m]

                self.assertEqual(mirror['title'], omirror.title)
                self.assertEqual(mirror['fileextension'],
                                 omirror.file_extension)

                # Test fields with potential default values
                self.assertEqual(mirror.get('position', default_position),
                                 omirror.position)
                self.assertEqual(mirror.get('tile_width', default_tile_width),
                                 omirror.tile_width)
                self.assertEqual(
                    mirror.get('tile_height', default_tile_height),
                    omirror.tile_height)
                self.assertEqual(
                    mirror.get('tile_source_type', default_tile_source_type),
                    omirror.tile_source_type)

                if 'url' in mirror:
                    image_base = mirror['url']
                else:
                    image_base = urljoin(
                        project_url,
                        urljoin(mirror.get('path', ''),
                                mirror.get('folder', '')))

                self.assertEqual(image_base, omirror.image_base)

            # Test project-stack link
            ps = ProjectStack.objects.get(project=p2.id, stack=p2s)
            six.assertCountEqual(
                self, literal_eval(stack.get('translation', '(0,0,0)')),
                literal_eval(str(ps.translation)))

            # Test stack groups
            ostack_group_links = StackStackGroup.objects.filter(
                stack=p2s).order_by('stack__title')
            stack_groups = stack.get('stackgroups', [])
            self.assertEqual(len(ostack_group_links), len(stack_groups))
            for m, sg_cfg in enumerate(stack_groups):
                ostack_group_link = ostack_group_links[m]
                ostack_group = ostack_group_link.stack_group
                self.assertEqual(sg_cfg['title'], ostack_group.title)
                self.assertEqual(sg_cfg['relation'],
                                 ostack_group_link.group_relation.name)
                self.assertEqual(sg_cfg.get('position', default_position),
                                 ostack_group_link.position)
Esempio n. 11
0
    def set_image_fields(self, info_object, project_url, data_folder,
                         needs_zoom):
        """ Sets the image_base, num_zoom_levels, file_extension fields
        of the calling object. Favor a URL field, if there is one. A URL
        field, however, also requires the existence of the
        'fileextension' and 'zoomlevels' field.
        """
        if 'url' in info_object:
            # Make sure all required data is available
            required_fields = [
                'fileextension',
            ]
            if needs_zoom:
                required_fields.append('zoomlevels')
            for f in required_fields:
                if f not in info_object:
                    raise RuntimeError("Missing required stack/overlay " \
                            "field '%s'" % f)
            # Read out data
            self.image_base = info_object['url']
            if needs_zoom:
                self.num_zoom_levels = info_object['zoomlevels']
            self.file_extension = info_object['fileextension']
        else:
            # The image base of a stack is the combination of the
            # project URL and the stack's folder name.
            folder = info_object['folder']
            self.image_base = urljoin(project_url, folder)
            # Favor 'zoomlevel' and 'fileextension' fields, if
            # available, but try to find this information if those
            # fields are not present.
            zoom_available = 'zoomlevels' in info_object
            ext_available = 'fileextension' in info_object
            if zoom_available and needs_zoom:
                self.num_zoom_levels = info_object['zoomlevels']
            if ext_available:
                self.file_extension = info_object['fileextension']
            # Only retrieve file extension and zoom level if one of
            # them is not available in the stack definition.
            if (not zoom_available and needs_zoom) or not ext_available:
                file_ext, zoom_levels = find_zoom_levels_and_file_ext(
                    data_folder, folder, needs_zoom)
                # If there is no zoom level provided, use the found one
                if not zoom_available and needs_zoom:
                    if not zoom_levels:
                        raise RuntimeError("Missing required stack/overlay " \
                                "field 'zoomlevels' and couldn't retrieve " \
                                "this information from image data.")
                    self.num_zoom_levels = zoom_levels
                # If there is no file extension level provided, use the
                # found one
                if not ext_available:
                    if not file_ext:
                        raise RuntimeError("Missing required stack/overlay " \
                                "field 'fileextension' and couldn't retrieve " \
                                "this information from image data.")
                    self.file_extension = file_ext

        # Make sure the image base has a trailing slash, because this is expected
        if self.image_base[-1] != '/':
            self.image_base = self.image_base + '/'

        # Test if the data is accessible through HTTP
        self.accessible = check_http_accessibility(self.image_base,
                                                   self.file_extension)
Esempio n. 12
0
    def test_import_projects(self):
        """Import a set of new projects, stacks and stack groups. This tests
        only the actual import. Retrieving the data to import from different
        sources is not part of this test.
        """
        project_url = 'https://catmaid-test/'
        data_folder = '/tmp/catmaid-test/'
        existing_projects = list(Project.objects.all())
        existing_project_ids = [p.id for p in existing_projects]

        p1_config = {
            'project': {
                'title': 'test-no-stacks',
            }
        }

        p2_config = {
            'project': {
                'title': 'test-two-stacks',
                'stacks': [
                    {
                        # A basic stack, only with required information
                        'title': 'test-stack-1',
                        'dimension': '(7, 17, 23)',
                        'resolution': '(2, 3, 5)',
                        'zoomlevels': -1,
                        'mirrors': [{
                            'title': 'test-mirror-1',
                            'fileextension': 'jpg'
                        }]
                    },
                    {
                        # A basic stack with a little more information
                        'title': 'test-stack-2',
                        'dimension': '(7, 17, 23)',
                        'resolution': '(2, 3, 5)',
                        'zoomlevels': -1,
                        'mirrors': [{
                            'title': 'test-mirror-2',
                            'fileextension': 'jpg',
                            'url': 'https://this.is.my.stack/'
                        }]
                    },
                    {
                        # A stack with all optional properties
                        'title': 'test-stack-3',
                        'dimension': '(4, 34, 9)',
                        'resolution': '(1, 2, 3)',
                        'metadata': 'Test meta data',
                        'zoomlevels': -1,
                        'translation': '(10, 20, 30)',
                        'mirrors': [{
                            'title': 'test-mirror-3',
                            'folder': 'abc/',
                            'fileextension': 'jpg',
                            'tile_width': 123,
                            'tile_height': 456,
                            'tile_source_type': 2,
                        }],
                        'stackgroups': [{
                            # Add a single stack group with only this stack
                            # in it.
                            'title': 'Test group 1',
                            'relation': 'view',
                        }],
                    }
                ]
            }
        }

        pre_projects = [
            importer.PreProject(p1_config, project_url, data_folder),
            importer.PreProject(p2_config, project_url, data_folder),
        ]

        tags = []
        permissions = []
        default_tile_width = 256
        default_tile_height = 512
        default_tile_source_type = 5
        default_position = 0
        cls_graph_ids_to_link = []
        remove_unref_stack_data = False

        imported, not_imported = importer.import_projects(self.user,
            pre_projects, tags, permissions, default_tile_width,
            default_tile_height, default_tile_source_type,
            cls_graph_ids_to_link, remove_unref_stack_data)

        self.assertListEqual(pre_projects, imported)
        self.assertListEqual([], not_imported)

        new_projects = list(Project.objects.exclude(id__in=existing_project_ids).order_by('title'))
        self.assertEqual(2, len(new_projects))

        # Projects should be ordered by name, so the first project will be based
        # on p1_config. Test p1 first, it is not expected to have any stacks.
        p1 = new_projects[0]
        self.assertEqual(p1_config['project']['title'], p1.title)
        self.assertEqual(0, p1.stacks.all().count())

        # Test p2.
        p2 = new_projects[1]
        self.assertEqual(p2_config['project']['title'], p2.title)
        p2_stacks = p2.stacks.all().order_by('title')
        self.assertEqual(3, len(p2_stacks))
        p2cfg_stacks = p2_config['project']['stacks']
        for n, p2s in enumerate(p2_stacks):
            stack = p2cfg_stacks[n]

            # Test required fields
            self.assertEqual(stack['title'], p2s.title)
            six.assertCountEqual(self, literal_eval(stack['dimension']),
                    literal_eval(str(p2s.dimension)))
            six.assertCountEqual(self, literal_eval(stack['resolution']),
                    literal_eval(str(p2s.resolution)))
            self.assertEqual(stack['zoomlevels'], p2s.num_zoom_levels)

            # Test mirrors
            mirrors = p2s.stackmirror_set.all().order_by('title')
            self.assertEqual(len(stack['mirrors']), len(mirrors))
            for m, omirror in enumerate(mirrors):
                mirror = stack['mirrors'][m]

                self.assertEqual(mirror['title'], omirror.title)
                self.assertEqual(mirror['fileextension'], omirror.file_extension)

                # Test fields with potential default values
                self.assertEqual(mirror.get('position', default_position),
                        omirror.position)
                self.assertEqual(mirror.get('tile_width', default_tile_width),
                        omirror.tile_width)
                self.assertEqual(mirror.get('tile_height', default_tile_height),
                        omirror.tile_height)
                self.assertEqual(mirror.get('tile_source_type', default_tile_source_type),
                        omirror.tile_source_type)

                if 'url' in mirror:
                    image_base = mirror['url']
                else:
                    image_base = urljoin(project_url,
                            urljoin(mirror.get('path', ''), mirror.get('folder', '')))

                self.assertEqual(image_base, omirror.image_base)

            # Test project-stack link
            ps = ProjectStack.objects.get(project=p2.id, stack=p2s)
            six.assertCountEqual(self, literal_eval(stack.get('translation', '(0,0,0)')),
                    literal_eval(str(ps.translation)))

            # Test stack groups
            ostack_group_links = StackStackGroup.objects.filter(stack=p2s).order_by('stack__title')
            stack_groups = stack.get('stackgroups', [])
            self.assertEqual(len(ostack_group_links), len(stack_groups))
            for m, sg_cfg in enumerate(stack_groups):
                ostack_group_link = ostack_group_links[m]
                ostack_group = ostack_group_link.stack_group
                self.assertEqual(sg_cfg['title'], ostack_group.title)
                self.assertEqual(sg_cfg['relation'],
                        ostack_group_link.group_relation.name)
                self.assertEqual(sg_cfg.get('position', default_position),
                        ostack_group_link.position)
Esempio n. 13
0
    def set_image_fields(self, info_object, project_url, data_folder, needs_zoom):
        """ Sets the image_base, num_zoom_levels, file_extension fields
        of the calling object. Favor a URL field, if there is one. A URL
        field, however, also requires the existence of the
        'fileextension' and 'zoomlevels' field.
        """
        if "url" in info_object:
            # Make sure all required data is available
            required_fields = ["fileextension"]
            if needs_zoom:
                required_fields.append("zoomlevels")
            for f in required_fields:
                if f not in info_object:
                    raise RuntimeError("Missing required stack/overlay " "field '%s'" % f)
            # Read out data
            self.image_base = info_object["url"]
            if needs_zoom:
                self.num_zoom_levels = info_object["zoomlevels"]
            self.file_extension = info_object["fileextension"]
        else:
            # The image base of a stack is the combination of the
            # project URL and the stack's folder name.
            folder = info_object["folder"]
            self.image_base = urljoin(project_url, folder)
            # Favor 'zoomlevel' and 'fileextension' fields, if
            # available, but try to find this information if those
            # fields are not present.
            zoom_available = "zoomlevels" in info_object
            ext_available = "fileextension" in info_object
            if zoom_available and needs_zoom:
                self.num_zoom_levels = info_object["zoomlevels"]
            if ext_available:
                self.file_extension = info_object["fileextension"]
            # Only retrieve file extension and zoom level if one of
            # them is not available in the stack definition.
            if (not zoom_available and needs_zoom) or not ext_available:
                file_ext, zoom_levels = find_zoom_levels_and_file_ext(data_folder, folder, needs_zoom)
                # If there is no zoom level provided, use the found one
                if not zoom_available and needs_zoom:
                    if not zoom_levels:
                        raise RuntimeError(
                            "Missing required stack/overlay "
                            "field 'zoomlevels' and couldn't retrieve "
                            "this information from image data."
                        )
                    self.num_zoom_levels = zoom_levels
                # If there is no file extension level provided, use the
                # found one
                if not ext_available:
                    if not file_ext:
                        raise RuntimeError(
                            "Missing required stack/overlay "
                            "field 'fileextension' and couldn't retrieve "
                            "this information from image data."
                        )
                    self.file_extension = file_ext

        # Make sure the image base has a trailing slash, because this is expected
        if self.image_base[-1] != "/":
            self.image_base = self.image_base + "/"

        # Test if the data is accessible through HTTP
        self.accessible = check_http_accessibility(self.image_base, self.file_extension)