class_docstr: A WMTS imagery retriever which can be added to a map.
  
  Uses tile caching for fast repeated map retrievals.
  
  Note
  ----
      Requires owslib and Pillow to work.
class_docstr_tok: ["A","WMTS","imagery","retriever","which","can","be","added","to","a","map",".","Uses","tile","caching","for","fast","repeated","map","retrievals",".","Note","--","--","Requires","owslib","and","Pillow","to","work","."]
code: def fetch_raster(self, projection, extent, target_resolution):
      matrix_set_name = self._matrix_set_name(projection)
      crs_urn = self.wmts.tilematrixsets[matrix_set_name].crs
      if crs_urn in _URN_TO_CRS:
          wmts_projection = _URN_TO_CRS[crs_urn]
      elif ":EPSG:" in crs_urn:
          epsg_num = crs_urn.split(":")[-1]
          wmts_projection = ccrs.epsg(int(epsg_num))
      else:
          raise ValueError(
              f"Unknown coordinate reference system string:"
              f" {crs_urn}"
          )
  
      if wmts_projection == projection:
          wmts_extents = [extent]
      else:
          # Calculate (possibly multiple) extents in the given projection.
          wmts_extents = _target_extents(
              extent, projection, wmts_projection
          )
          # Bump resolution by a small factor, as a weak alternative to
          # delivering a minimum projected resolution.
          # Generally, the desired area is smaller than the enclosing extent
          # in projection space and may have varying scaling, so the ideal
          # solution is a hard problem !
          resolution_factor = 1.4
          target_resolution = (
              np.array(target_resolution) * resolution_factor
          )
  
      width, height = target_resolution
      located_images = []
      for wmts_desired_extent in wmts_extents:
          # Calculate target resolution for the actual polygon.  Note that
          # this gives *every* polygon enough pixels for the whole result,
          # which is potentially excessive!
          min_x, max_x, min_y, max_y = wmts_desired_extent
          if wmts_projection == projection:
              max_pixel_span = min(
                  (max_x - min_x) / width, (max_y - min_y) / height
              )
          else:
              # X/Y orientation is arbitrary, so use a worst-case guess.
              max_pixel_span = min(max_x - min_x, max_y - min_y) / max(
                  width, height
              )
  
          # Fetch a suitable image and its actual extent.
          wmts_image, wmts_actual_extent = self._wmts_images(
              self.wmts,
              self.layer,
              matrix_set_name,
              extent=wmts_desired_extent,
              max_pixel_span=max_pixel_span,
          )
  
          # Return each (image, extent) as a LocatedImage.
          if wmts_projection == projection:
              located_image = LocatedImage(
                  wmts_image, wmts_actual_extent
              )
          else:
              # Reproject the image to the desired projection.
              located_image = _warped_located_image(
                  wmts_image,
                  wmts_projection,
                  wmts_actual_extent,
                  output_projection=projection,
                  output_extent=extent,
                  target_resolution=target_resolution,
              )
  
          located_images.append(located_image)
  
      return located_images
  
imports: import collections
  import io
  import math
  from urllib.parse import urlparse
  import warnings
  import weakref
  from xml.etree import ElementTree
  import numpy
  from PIL import Image
  import shapely.geometry
  import cartopy.crs
  from cartopy.img_transform import warp_array
  from cartopy.io import LocatedImage, RasterSource
label_desc: Use image node_id 4 for calling the WMTSRasterSource obj's underlying member method code with example usage: obj.fetch_raster(projection, extent, target_resolution) and returns: located_images