byte[] toBytes(WebMap map) throws IOException {
    ByteArrayOutputStream bout = new ByteArrayOutputStream();

    if (map instanceof RenderedImageMap) {
      RenderedImageMapResponse response =
          JPEG_MIME_TYPE.equals(map.getMimeType())
              ? new JPEGMapResponse(wms)
              : new PNGMapResponse(wms);
      response.write(map, bout, null);
    } else if (map instanceof RawMap) {
      ((RawMap) map).writeTo(bout);
    }
    bout.flush();
    return bout.toByteArray();
  }
  @Override
  public WebMap produceMap(WMSMapContent map) throws ServiceException, IOException {
    GeoPackage geopkg = new GeoPackage();
    geopkg.init();

    GetMapRequest req = map.getRequest();

    List<Layer> layers = map.layers();
    List<MapLayerInfo> mapLayers = req.getLayers();

    Preconditions.checkState(
        layers.size() == mapLayers.size(),
        "Number of map layers not same as number of rendered layers");

    // list of layers to render directly and include as tiles
    List<MapLayerInfo> tileLayers = new ArrayList();

    // check mode, one of:
    // vector - render vector layers as feature entries and all else as tiles (default)
    // hybrid - render vector layers as feature entries, raster layers as raster entries, all
    //          others as tile entries
    // tiled - all layers as a single tile set
    Map formatOpts = req.getFormatOptions();
    Mode mode =
        formatOpts.containsKey("mode")
            ? Mode.valueOf(((String) formatOpts.get("mode")).toUpperCase())
            : Mode.VECTOR;

    if (mode == Mode.TILED) {
      // tiled mode means render all as map tile layer
      tileLayers.addAll(mapLayers);
    } else {

      // hybrid mode, dump as raw vector or raster unless the request specifically asks for a
      // layer to be rendered as tiles
      for (int i = 0; i < layers.size(); i++) {
        Layer layer = layers.get(i);
        MapLayerInfo mapLayer = mapLayers.get(i);

        if (layer instanceof FeatureLayer) {
          addFeatureLayer(geopkg, (FeatureLayer) layer, mapLayer, map);
        } else if (layer instanceof GridCoverageLayer) {
          if (mode == Mode.HYBRID) {
            addCoverageLayer(geopkg, (GridCoverageLayer) layer, mapLayer, map);
          } else {
            tileLayers.add(mapLayer);
          }
        } else {
          tileLayers.add(mapLayer);
        }
      }
    }

    addTileLayers(geopkg, tileLayers, map);

    geopkg.close();

    final File dbFile = geopkg.getFile();
    final BufferedInputStream bin = new BufferedInputStream(new FileInputStream(dbFile));

    RawMap result =
        new RawMap(map, bin, MIME_TYPE) {
          @Override
          public void writeTo(OutputStream out) throws IOException {
            String dbFilename = getAttachmentFileName();
            if (dbFilename != null) {
              dbFilename = dbFilename.substring(0, dbFilename.length() - 4) + ".gpkg";
            } else {
              // this shouldn't really ever happen, but fallback anyways
              dbFilename = "geoserver.gpkg";
            }

            IOUtils.copy(bin, out);
            out.flush();

            //               JD: disabling zip compression for now
            //                ZipOutputStream zout = new ZipOutputStream(out);
            //                zout.putNextEntry(new ZipEntry(dbFilename));
            //
            //                super.writeTo(zout);
            //                zout.closeEntry();
            //                zout.close();

            bin.close();
            try {
              dbFile.delete();
            } catch (Exception e) {
              LOGGER.log(Level.WARNING, "Error deleting file: " + dbFile.getAbsolutePath(), e);
            }
          }
        };

    result.setContentDispositionHeader(map, ".gpkg", true);
    return result;
  }