Beispiel #1
0
  protected void determineSourceResolution() {
    xResolution = reqBounds.getWidth() / reqWidth;
    yResolution = reqBounds.getHeight() / reqHeight;

    double tmpResolution;
    // We use the smallest one
    if (yResolution < xResolution) {
      tmpResolution = yResolution;
    } else {
      tmpResolution = xResolution;
    }

    log.debug(
        "x res: " + xResolution + " y res: " + yResolution + " tmpResolution: " + tmpResolution);

    // Cut ourselves 0.5% slack
    double compResolution = 1.005 * tmpResolution;

    double[] resArray = gridSubset.getResolutions();

    for (srcIdx = 0; srcIdx < resArray.length; srcIdx++) {
      srcResolution = resArray[srcIdx];
      if (srcResolution < compResolution) {
        break;
      }
    }

    if (srcIdx >= resArray.length) {
      srcIdx = resArray.length - 1;
    }

    log.debug("z: " + srcIdx + " , resolution: " + srcResolution + " (" + tmpResolution + ")");

    // At worst, we have the best resolution possible
  }
 String[] boundsPrep(BoundingBox bbox) {
   String[] bs = {
     Double.toString(bbox.getMinX()),
     Double.toString(bbox.getMinY()),
     Double.toString(bbox.getMaxX()),
     Double.toString(bbox.getMaxY())
   };
   return bs;
 }
Beispiel #3
0
  /**
   * Creates the {@link RenderedImage} corresponding to the tile at index {@code tileIdx} and uses a
   * {@link RenderedImageMapResponse} to encode it into the {@link #getResponseFormat() response
   * format}.
   *
   * @see org.geowebcache.layer.MetaTile#writeTileToStream(int, org.geowebcache.io.Resource)
   * @see RenderedImageMapResponse#write
   */
  @Override
  public boolean writeTileToStream(final int tileIdx, Resource target) throws IOException {

    checkNotNull(metaTileMap, "webMap is not set");
    if (!(metaTileMap instanceof RenderedImageMap)) {
      throw new IllegalArgumentException(
          "Only RenderedImageMaps are supported so far: " + metaTileMap.getClass().getName());
    }
    final RenderedImageMapResponse mapEncoder;
    {
      final GWC mediator = GWC.get();
      final Response responseEncoder = mediator.getResponseEncoder(responseFormat, metaTileMap);
      mapEncoder = (RenderedImageMapResponse) responseEncoder;
    }

    RenderedImage tile = metaTileMap.getImage();
    WMSMapContent tileContext = metaTileMap.getMapContext();

    if (this.tiles.length > 1 || (this.tiles.length == 1 && metaHasGutter())) {
      final Rectangle tileDim = this.tiles[tileIdx];
      tile = createTile(tileDim.x, tileDim.y, tileDim.width, tileDim.height);
      disposeLater(tile);
      {
        final WMSMapContent metaTileContext = metaTileMap.getMapContext();
        // do not create tileContext with metaTileContext.getLayers() as the layer list.
        // It is not needed at this stage and the constructor would force a
        // MapLayer.getBounds() that might fail
        tileContext = new WMSMapContent();
        tileContext.setRequest(metaTileContext.getRequest());
        tileContext.setBgColor(metaTileContext.getBgColor());
        tileContext.setMapWidth(tileDim.width);
        tileContext.setMapHeight(tileDim.height);
        tileContext.setPalette(metaTileContext.getPalette());
        tileContext.setTransparent(tileContext.isTransparent());
        long[][] tileIndexes = getTilesGridPositions();
        BoundingBox tileBounds = gridSubset.boundsFromIndex(tileIndexes[tileIdx]);
        ReferencedEnvelope tilebbox =
            new ReferencedEnvelope(metaTileContext.getCoordinateReferenceSystem());
        tilebbox.init(
            tileBounds.getMinX(), tileBounds.getMaxX(), tileBounds.getMinY(), tileBounds.getMaxY());
        tileContext.getViewport().setBounds(tilebbox);
      }
    }

    OutputStream outStream = target.getOutputStream();
    try {
      // call formatImageOuputStream instead of write to avoid disposition of rendered images
      // when processing a tile from a metatile and instead defer it to this class' dispose()
      // method
      mapEncoder.formatImageOutputStream(tile, outStream, tileContext);
      return true;
    } finally {
      outStream.close();
    }
  }
  Envelope findTileBounds(GridSubset gridSubset, BoundingBox bbox, int z) {

    long[] i = gridSubset.getCoverageIntersection(z, bbox);

    BoundingBox b1 = gridSubset.boundsFromIndex(new long[] {i[0], i[1], i[4]});
    BoundingBox b2 = gridSubset.boundsFromIndex(new long[] {i[2], i[3], i[4]});
    return new Envelope(
        Math.min(b1.getMinX(), b2.getMinX()),
        Math.max(b1.getMaxX(), b2.getMaxX()),
        Math.min(b1.getMinY(), b2.getMinY()),
        Math.max(b1.getMaxY(), b2.getMaxY()));
  }
Beispiel #5
0
    @Override
    public void validate(IValidatable<Set<XMLGridSubset>> validatable) {
      if (!validate) {
        return;
      }
      Set<XMLGridSubset> gridSubsets = validatable.getValue();
      if (gridSubsets == null || gridSubsets.size() == 0) {
        error(validatable, "GridSubsetsEditor.validation.empty");
        return;
      }

      final GWC gwc = GWC.get();
      for (XMLGridSubset subset : gridSubsets) {
        final String gridSetName = subset.getGridSetName();
        final Integer zoomStart = subset.getZoomStart();
        final Integer zoomStop = subset.getZoomStop();
        final BoundingBox extent = subset.getExtent();

        if (gridSetName == null) {
          throw new IllegalStateException("GridSet name is null");
        }

        if (zoomStart != null && zoomStop != null) {
          if (zoomStart.intValue() > zoomStop.intValue()) {
            error(validatable, "GridSubsetsEditor.validation.zoomLevelsError");
            return;
          }
        }

        final GridSetBroker gridSetBroker = gwc.getGridSetBroker();
        final GridSet gridSet = gridSetBroker.get(gridSetName);

        if (null == gridSet) {
          error(validatable, "GridSubsetsEditor.validation.gridSetNotFound", gridSetName);
          return;
        }

        if (extent != null) {
          if (extent.isNull() || !extent.isSane()) {
            error(validatable, "GridSubsetsEditor.validation.invalidBounds");
          }
          final BoundingBox fullBounds = gridSet.getOriginalExtent();
          final boolean intersects = fullBounds.intersects(extent);
          if (!intersects) {
            error(validatable, "GridSubsetsEditor.validation.boundsOutsideCoverage");
          }
        }
      }
    }
Beispiel #6
0
  private void testMultipleCrsMatchingGridSubsets(
      final String srs, final String expectedGridset, long[] tileIndex) throws Exception {
    GetMapRequest request = new GetMapRequest();

    @SuppressWarnings("unchecked")
    Map<String, String> rawKvp = new CaseInsensitiveMap(new HashMap<String, String>());
    request.setRawKvp(rawKvp);
    request.setFormat("image/png");

    request.setSRS(srs);

    request.setWidth(256);
    request.setHeight(256);
    rawKvp.put("layers", "mockLayer");
    List<String> gridSetNames = Arrays.asList("GlobalCRS84Pixel", "GlobalCRS84Scale", "EPSG:4326");
    tileLayer = mockTileLayer("mockLayer", gridSetNames);

    // make the request match a tile in the expected gridset
    BoundingBox bounds;
    bounds = tileLayer.getGridSubset(expectedGridset).boundsFromIndex(tileIndex);

    Envelope reqBbox =
        new Envelope(bounds.getMinX(), bounds.getMaxX(), bounds.getMinY(), bounds.getMaxY());
    request.setBbox(reqBbox);

    ArgumentCaptor<ConveyorTile> captor = ArgumentCaptor.forClass(ConveyorTile.class);
    StringBuilder errors = new StringBuilder();

    mediator.dispatch(request, errors);

    assertTrue(errors.toString(), errors.length() == 0);

    verify(tileLayer, times(1)).getTile(captor.capture());

    ConveyorTile tileRequest = captor.getValue();

    assertEquals(expectedGridset, tileRequest.getGridSetId());
    assertEquals("image/png", tileRequest.getMimeType().getMimeType());
    assertTrue(
        "Expected "
            + Arrays.toString(tileIndex)
            + " got "
            + Arrays.toString(tileRequest.getTileIndex()),
        Arrays.equals(tileIndex, tileRequest.getTileIndex()));
  }
Beispiel #7
0
  @Test
  public void testDispatchGetMapWithMatchingParameterFilters() throws Exception {
    GetMapRequest request = new GetMapRequest();

    @SuppressWarnings("unchecked")
    Map<String, String> rawKvp = new CaseInsensitiveMap(new HashMap<String, String>());
    request.setRawKvp(rawKvp);
    request.setFormat("image/png");
    request.setSRS("EPSG:900913");
    request.setWidth(256);
    request.setHeight(256);
    rawKvp.put("layers", tileLayer.getName());

    // tileLayer = mockTileLayer("mockLayer", ImmutableList.of("EPSG:900913", "EPSG:4326"));

    // make the request match a tile in the expected gridset
    BoundingBox bounds;
    bounds = tileLayer.getGridSubset("EPSG:900913").boundsFromIndex(new long[] {0, 0, 1});

    Envelope reqBbox =
        new Envelope(bounds.getMinX(), bounds.getMaxX(), bounds.getMinY(), bounds.getMaxY());
    request.setBbox(reqBbox);

    assertTrue(tileLayer.getInfo().cachedStyles().size() > 0);

    for (String style : tileLayer.getInfo().cachedStyles()) {

      String rawKvpParamName = "styles";
      String rawKvpParamValue = style;

      testParameterFilter(request, rawKvp, rawKvpParamName, rawKvpParamValue);
    }

    request.setEnv(ImmutableMap.of("envKey", "envValue"));
    updateStringParameterFilter(
        tileLayerInfo, "ENV", true, "def:devVal", "envKey:envValue", "envKey2:envValue2");
    testParameterFilter(request, rawKvp, "env", "envKey:envValue");

    updateAcceptAllFloatParameterFilter(tileLayerInfo, "ANGLE", true);
    request.setAngle(60);
    testParameterFilter(request, rawKvp, "angle", "60.0");

    request.setAngle(61.1);
    testParameterFilter(request, rawKvp, "angle", "61.1");
  }
Beispiel #8
0
  @Test
  public void testTruncateByBounds() throws Exception {

    String layerName = tileLayer.getName();
    ReferencedEnvelope bounds;
    // bounds outside layer bounds (which are -180,0,0,90)
    bounds = new ReferencedEnvelope(10, 20, 10, 20, DefaultGeographicCRS.WGS84);
    BoundingBox layerBounds = tileLayer.getGridSubset("EPSG:4326").getGridSet().getOriginalExtent();

    assertFalse(bounds.intersects(layerBounds.getMinX(), layerBounds.getMinY()));
    assertFalse(bounds.intersects(layerBounds.getMaxX(), layerBounds.getMaxY()));

    mediator.truncate(layerName, bounds);

    verify(tileBreeder, never()).dispatchTasks(any(GWCTask[].class));

    // bounds intersecting layer bounds
    bounds = new ReferencedEnvelope(-10, -10, 10, 10, DefaultGeographicCRS.WGS84);

    mediator.truncate(layerName, bounds);

    int numGridsets = tileLayer.getGridSubsets().size();
    int numFormats = tileLayer.getMimeTypes().size();
    int numStyles = 1 /* default */ + tileLayer.getInfo().cachedStyles().size();
    final int expected = numGridsets * numFormats * numStyles;
    verify(tileBreeder, times(expected)).dispatchTasks(any(GWCTask[].class));

    reset(tileBreeder);
    bounds = bounds.transform(CRS.decode("EPSG:900913"), true);
    mediator.truncate(layerName, bounds);
    verify(tileBreeder, times(expected)).dispatchTasks(any(GWCTask[].class));

    reset(tileBreeder);
    bounds = mediator.getAreaOfValidity(CRS.decode("EPSG:2083")); // Terra del Fuego
    mediator.truncate(layerName, bounds);
    verify(tileBreeder, never()).dispatchTasks(any(GWCTask[].class));

    reset(tileBreeder);
    bounds = mediator.getAreaOfValidity(CRS.decode("EPSG:26986")); // Massachussets
    mediator.truncate(layerName, bounds);
    verify(tileBreeder, times(expected)).dispatchTasks(any(GWCTask[].class));
  }
Beispiel #9
0
  private static String generateHTML(
      TileLayer layer, String gridSetStr, String formatStr, boolean asPlugin)
      throws GeoWebCacheException {
    String layerName = layer.getName();

    GridSubset gridSubset = layer.getGridSubset(gridSetStr);

    BoundingBox bbox = gridSubset.getGridSetBounds();
    BoundingBox zoomBounds = gridSubset.getOriginalExtent();

    String res = "resolutions: " + Arrays.toString(gridSubset.getResolutions()) + ",\n";

    String units = "units: \"" + gridSubset.getGridSet().guessMapUnits() + "\",\n";

    String openLayersPath;
    if (asPlugin) {
      openLayersPath = "../openlayers/OpenLayers.js";
    } else {
      openLayersPath = "../openlayers/OpenLayers.js";
    }

    String page =
        "<html xmlns=\"http://www.w3.org/1999/xhtml\"><head>\n"
            + "<meta http-equiv=\"imagetoolbar\" content=\"no\">\n"
            + "<title>"
            + layerName
            + " "
            + gridSubset.getName()
            + " "
            + formatStr
            + "</title>\n"
            + "<style type=\"text/css\">\n"
            + "body { font-family: sans-serif; font-weight: bold; font-size: .8em; }\n"
            + "body { border: 0px; margin: 0px; padding: 0px; }\n"
            + "#map { width: 85%; height: 85%; border: 0px; padding: 0px; }\n"
            + "</style>\n"
            + "<script src=\""
            + openLayersPath
            + "\"></script>    \n"
            + "<script type=\"text/javascript\">               \n"
            + "var map, demolayer;                               \n"
            + "  // sets the chosen modifiable parameter        \n"
            + "  function setParam(name, value){                \n"
            + "   str = \"demolayer.mergeNewParams({\" + name + \": '\" + value + \"'})\" \n"
            + "   // alert(str);                                   \n"
            + "   eval(str);                                    \n"
            + "  }                                              \n"
            + "OpenLayers.DOTS_PER_INCH = "
            + gridSubset.getDotsPerInch()
            + ";\n"
            + "OpenLayers.Util.onImageLoadErrorColor = 'transparent';\n"
            + "function init(){\n"
            + "var mapOptions = { \n"
            + res
            + "projection: new OpenLayers.Projection('"
            + gridSubset.getSRS().toString()
            + "'),\n"
            + "maxExtent: new OpenLayers.Bounds("
            + bbox.toString()
            + "),\n"
            + units
            + "controls: []\n"
            + "};\n"
            + "map = new OpenLayers.Map('map', mapOptions );\n"
            + "map.addControl(new OpenLayers.Control.PanZoomBar({\n"
            + "		position: new OpenLayers.Pixel(2, 15)\n"
            + "}));\n"
            + "map.addControl(new OpenLayers.Control.Navigation());\n"
            + "map.addControl(new OpenLayers.Control.Scale($('scale')));\n"
            + "map.addControl(new OpenLayers.Control.MousePosition({element: $('location')}));\n"
            + "demolayer = new OpenLayers.Layer.WMS(\n"
            + "\""
            + layerName
            + "\",\"../service/wms\",\n"
            + "{layers: '"
            + layerName
            + "', format: '"
            + formatStr
            + "' },\n"
            + "{ tileSize: new OpenLayers.Size("
            + gridSubset.getTileWidth()
            + ","
            + gridSubset.getTileHeight()
            + ")";

    /*
     * If the gridset has a top left tile origin, lets tell that to open layers. Otherwise it'll
     * calculate tile bounds based on the bbox bottom left corner, leading to misaligned
     * requests.
     */
    GridSet gridSet = gridSubset.getGridSet();
    if (gridSet.isTopLeftAligned()) {
      page +=
          ",\n tileOrigin: new OpenLayers.LonLat(" + bbox.getMinX() + ", " + bbox.getMaxY() + ")";
    }

    page +=
        "});\n"
            + "map.addLayer(demolayer);\n"
            + "map.zoomToExtent(new OpenLayers.Bounds("
            + zoomBounds.toString()
            + "));\n"
            + "// The following is just for GetFeatureInfo, which is not cached. Most people do not need this \n"
            + "map.events.register('click', map, function (e) {\n"
            + "  document.getElementById('nodelist').innerHTML = \"Loading... please wait...\";\n"
            + "  var params = {\n"
            + "    REQUEST: \"GetFeatureInfo\",\n"
            + "    EXCEPTIONS: \"application/vnd.ogc.se_xml\",\n"
            + "    BBOX: map.getExtent().toBBOX(),\n"
            + "    X: e.xy.x,\n"
            + "    Y: e.xy.y,\n"
            + "    INFO_FORMAT: 'text/html',\n"
            + "    QUERY_LAYERS: map.layers[0].params.LAYERS,\n"
            + "    FEATURE_COUNT: 50,\n"
            + "    Layers: '"
            + layerName
            + "',\n"
            + "    Styles: '',\n"
            + "    Srs: '"
            + gridSubset.getSRS().toString()
            + "',\n"
            + "    WIDTH: map.size.w,\n"
            + "    HEIGHT: map.size.h,\n"
            + "    format: \""
            + formatStr
            + "\" };\n"
            + "  OpenLayers.loadURL(\"../service/wms\", params, this, setHTML, setHTML);\n"
            + "  OpenLayers.Event.stop(e);\n"
            + "  });\n"
            + "}\n"
            + "function setHTML(response){\n"
            + "    document.getElementById('nodelist').innerHTML = response.responseText;\n"
            + "};\n"
            + "</script>\n"
            + "</head>\n"
            + "<body onload=\"init()\">\n"
            + "<div id=\"params\">"
            + makeModifiableParameters(layer)
            + "</div>\n"
            + "<div id=\"map\"></div>\n"
            + "<div id=\"nodelist\"></div>\n"
            + "</body>\n"
            + "</html>";
    return page;
  }
Beispiel #10
0
  protected void determineCanvasLayout() {
    // Find the spatial extent of the tiles needed to cover the desired extent
    srcRectangle = gridSubset.getCoverageIntersection(srcIdx, reqBounds);
    srcBounds = gridSubset.boundsFromRectangle(srcRectangle);

    // We now have the complete area, lets figure out our offsets
    // Positive means that there is blank space to the first tile,
    // negative means we will not use the entire tile
    boundOfs.left = srcBounds.getMinX() - reqBounds.getMinX();
    boundOfs.bottom = srcBounds.getMinY() - reqBounds.getMinY();
    boundOfs.right = reqBounds.getMaxX() - srcBounds.getMaxX();
    boundOfs.top = reqBounds.getMaxY() - srcBounds.getMaxY();

    canvasSize[0] = (int) Math.round(reqBounds.getWidth() / this.srcResolution);
    canvasSize[1] = (int) Math.round(reqBounds.getHeight() / this.srcResolution);

    PixelOffsets naiveOfs = new PixelOffsets();
    // Calculate the corresponding pixel offsets. We'll stick to sane,
    // i.e. bottom left, coordinates at this point
    naiveOfs.left = (int) Math.round(boundOfs.left / this.srcResolution);
    naiveOfs.bottom = (int) Math.round(boundOfs.bottom / this.srcResolution);
    naiveOfs.right = (int) Math.round(boundOfs.right / this.srcResolution);
    naiveOfs.top = (int) Math.round(boundOfs.top / this.srcResolution);

    // Find the offsets on the opposite sides.  This is dependent of how the first two were rounded.

    // First, find a tile boundary near the canvas edge, then make sure it's on the correct
    // side to match the corresponding boundOfs, then take the modulo of the naive rounding
    // based on the boundOfs, then subtract the two and apply the difference to the boundOfs.
    int tileWidth = this.gridSubset.getTileWidth();
    int tileHeight = this.gridSubset.getTileHeight();

    canvOfs.left = naiveOfs.left;
    canvOfs.bottom = naiveOfs.bottom;

    canvOfs.right = (canvasSize[0] - canvOfs.left) % tileWidth; // Find nearby tile boundary
    canvOfs.right =
        (Integer.signum(naiveOfs.right) * tileWidth + canvOfs.right)
            % tileWidth; // Ensure same sign as naive calculation
    canvOfs.right =
        canvOfs.right
            - (naiveOfs.right % tileWidth)
            + naiveOfs.right; // Find adjustment from naive and apply to naive calculation
    canvOfs.top = (canvasSize[1] - canvOfs.bottom) % tileHeight; // Find nearby tile boundary
    canvOfs.top =
        (Integer.signum(naiveOfs.top) * tileHeight + canvOfs.top)
            % tileHeight; // Ensure same sign as naive calculation
    canvOfs.top =
        canvOfs.top
            - (naiveOfs.top % tileHeight)
            + naiveOfs.top; // Find adjustment from naive and apply to naive calculation

    // postconditions
    assert Math.abs(canvOfs.left - naiveOfs.left) <= 1;
    assert Math.abs(canvOfs.bottom - naiveOfs.bottom) <= 1;
    assert Math.abs(canvOfs.right - naiveOfs.right) <= 1;
    assert Math.abs(canvOfs.top - naiveOfs.top) <= 1;

    if (log.isDebugEnabled()) {
      log.debug("intersection rectangle: " + Arrays.toString(srcRectangle));
      log.debug("intersection bounds: " + srcBounds + " (" + reqBounds + ")");
      log.debug(
          "Bound offsets: "
              + Arrays.toString(
                  new double[] {boundOfs.left, boundOfs.bottom, boundOfs.right, boundOfs.top}));
      log.debug(
          "Canvas size: " + Arrays.toString(canvasSize) + "(" + reqWidth + "," + reqHeight + ")");
      log.debug(
          "Canvas offsets: "
              + Arrays.toString(
                  new int[] {canvOfs.left, canvOfs.bottom, canvOfs.right, canvOfs.top}));
    }
  }
  void addTileLayers(GeoPackage geopkg, List<MapLayerInfo> mapLayers, WMSMapContent map)
      throws IOException {

    if (mapLayers.isEmpty()) {
      return;
    }

    // figure out a name for the file entry
    String tileEntryName = null;
    Map formatOpts = map.getRequest().getFormatOptions();
    if (formatOpts.containsKey("tileset_name")) {
      tileEntryName = (String) formatOpts.get("tileset_name");
    }
    if (tileEntryName == null) {
      tileEntryName = map.getTitle();
    }
    if (tileEntryName == null && mapLayers.size() == 1) {
      Iterator<MapLayerInfo> it = mapLayers.iterator();
      tileEntryName = it.next().getLayerInfo().getName();
    }

    GridSubset gridSubset = findBestGridSubset(map);
    int[] minmax = findMinMaxZoom(gridSubset, map);

    BoundingBox bbox = bbox(map);

    TileEntry e = new TileEntry();
    e.setTableName(tileEntryName);

    if (mapLayers.size() == 1) {
      ResourceInfo r = mapLayers.get(0).getResource();
      e.setIdentifier(r.getTitle());
      e.setDescription(r.getAbstract());
    }
    e.setBounds(
        new ReferencedEnvelope(
            findTileBounds(gridSubset, bbox, minmax[0]), map.getCoordinateReferenceSystem()));
    e.setSrid(srid(map));

    GridSet gridSet = gridSubset.getGridSet();
    for (int z = minmax[0]; z < minmax[1]; z++) {
      Grid g = gridSet.getGrid(z);

      TileMatrix m = new TileMatrix();
      m.setZoomLevel(z);
      m.setMatrixWidth((int) g.getNumTilesWide());
      m.setMatrixHeight((int) g.getNumTilesHigh());
      m.setTileWidth(gridSubset.getTileWidth());
      m.setTileHeight(gridSubset.getTileHeight());

      // TODO: not sure about this
      m.setXPixelSize(g.getResolution());
      m.setYPixelSize(g.getResolution());
      // m.setXPixelSize(gridSet.getPixelSize());
      // m.setYPixelSize(gridSet.getPixelSize());

      e.getTileMatricies().add(m);
    }

    // figure out the actual bounds of the tiles to be renderered
    LOGGER.fine("Creating tile entry" + e.getTableName());
    geopkg.create(e);

    // create a prototype getmap request
    GetMapRequest req = new GetMapRequest();
    OwsUtils.copy(map.getRequest(), req, GetMapRequest.class);
    req.setLayers(mapLayers);

    String imageFormat =
        formatOpts.containsKey("format") ? parseFormatFromOpts(formatOpts) : findBestFormat(map);

    req.setFormat(imageFormat);
    req.setWidth(gridSubset.getTileWidth());
    req.setHeight(gridSubset.getTileHeight());

    // count tiles as we generate them
    int ntiles = 0;

    // flag determining if tile row indexes we store in database should be inverted
    boolean flipy = Boolean.valueOf((String) formatOpts.get("flipy"));
    for (int z = minmax[0]; z < minmax[1]; z++) {
      long[] intersect = gridSubset.getCoverageIntersection(z, bbox);
      for (long x = intersect[0]; x <= intersect[2]; x++) {
        for (long y = intersect[1]; y <= intersect[3]; y++) {
          BoundingBox box = gridSubset.boundsFromIndex(new long[] {x, y, z});
          req.setBbox(new Envelope(box.getMinX(), box.getMaxX(), box.getMinY(), box.getMaxY()));

          Tile t = new Tile();
          t.setZoom(z);
          t.setColumn((int) x);
          t.setRow((int) (flipy ? gridSubset.getNumTilesHigh(z) - (y + 1) : y));

          WebMap result = webMapService.getMap(req);
          t.setData(toBytes(result));

          geopkg.add(e, t);

          // images we encode are actually kept around, we need to clean them up
          if (ntiles++ == TILE_CLEANUP_INTERVAL) {
            cleanUpImages();
            ntiles = 0;
          }
        }
      }
    }
  }