def _load_tiles(self):
        # recreate source to assure the source belongs to the new thread, SQLite3 isn't happy about it otherwise
        self._source = self._create_source(self.connection())

        try:
            if can_load_lib():
                info("Native decoding supported!!!")
            else:
                bits = "32"
                if sys.maxsize > 2**32:
                    bits = "64"
                info("Native decoding not supported: {}, {}bit", sys.platform,
                     bits)

            self._feature_count = 0
            self._all_tiles = []

            bounds = self._loading_options["bounds"]
            clip_tiles = self._loading_options["clip_tiles"]
            max_tiles = self._loading_options["max_tiles"]
            layer_filter = self._loading_options["layer_filter"]
            info("Tile limit enabled: {}", max_tiles is not None
                 and max_tiles > 0)
            self.cancel_requested = False
            self.feature_collections_by_layer_name_and_geotype = {}
            self._update_progress(show_dialog=True)
            self._clip_tiles_at_tile_bounds = clip_tiles

            zoom_level = self._get_clamped_zoom_level()

            all_tiles = get_all_tiles(
                bounds=bounds,
                is_cancel_requested_handler=lambda: self.cancel_requested,
            )
            tiles_to_load = set()
            cached_tiles = []
            tiles_to_ignore = set()
            source_name = self._source.name()
            scheme = self._source.scheme()
            for t in all_tiles:
                if self.cancel_requested or (max_tiles and
                                             len(cached_tiles) >= max_tiles):
                    break

                decoded_data = get_cache_entry(cache_name=source_name,
                                               zoom_level=zoom_level,
                                               x=t[0],
                                               y=t[1])
                if decoded_data:
                    tile = VectorTile(scheme=scheme,
                                      zoom_level=zoom_level,
                                      x=t[0],
                                      y=t[1])
                    tile.decoded_data = decoded_data
                    cached_tiles.append(tile)
                    tiles_to_ignore.add((tile.column, tile.row))
                else:
                    tiles_to_load.add(t)

            remaining_nr_of_tiles = len(tiles_to_load)
            if max_tiles:
                if len(cached_tiles) + len(tiles_to_load) >= max_tiles:
                    remaining_nr_of_tiles = clamp(max_tiles -
                                                  len(cached_tiles),
                                                  low=0)
            info("{} tiles in cache. Max. {} will be loaded additionally.",
                 len(cached_tiles), remaining_nr_of_tiles)
            if len(cached_tiles) > 0:
                if not self.cancel_requested:
                    self._process_tiles(cached_tiles, layer_filter)
                    self._all_tiles.extend(cached_tiles)

            debug("Loading data for zoom level '{}' source '{}'", zoom_level,
                  self._source.name())

            if remaining_nr_of_tiles:
                tile_data_tuples = self._source.load_tiles(
                    zoom_level=zoom_level,
                    tiles_to_load=tiles_to_load,
                    max_tiles=remaining_nr_of_tiles)
                if len(tile_data_tuples) > 0 and not self.cancel_requested:
                    tiles = self._decode_tiles(tile_data_tuples)
                    self._process_tiles(tiles, layer_filter)
                    for t in tiles:
                        cache_tile(cache_name=source_name,
                                   zoom_level=zoom_level,
                                   x=t.column,
                                   y=t.row,
                                   decoded_data=t.decoded_data)
                    self._all_tiles.extend(tiles)
            self._continue_loading()

        except Exception as e:
            tb = ""
            if traceback:
                tb = traceback.format_exc()
            critical("An exception occured: {}, {}", e, tb)
            self.cancelled.emit()
 def _get_clamped_zoom_level(self):
     zoom_level = self._loading_options["zoom_level"]
     min_zoom = self._source.min_zoom()
     max_zoom = self._source.max_zoom()
     zoom_level = clamp(zoom_level, low=min_zoom, high=max_zoom)
     return zoom_level