class GeoPackageImageEnvoy extends AbstractEnvoy implements DataRegistryDataProvider {

    @Override
    public void query(DataModelCategory category, Collection<? extends Satisfaction> satisfactions,
            List<? extends PropertyMatcher<?>> parameters, List<? extends OrderSpecifier> orderSpecifiers, int limit,
            Collection<? extends PropertyDescriptor<?>> propertyDescriptors, CacheDepositReceiver queryReceiver)
        throws InterruptedException, QueryException
    {
        if (parameters.size() != 1 || !(parameters.get(0) instanceof ZYXKeyPropertyMatcher))
        {
            throw new IllegalArgumentException(ZYXKeyPropertyMatcher.class.getSimpleName() + " was not found in parameters.");
        }

        String layer = category.getFamily();

        ZYXKeyPropertyMatcher param = (ZYXKeyPropertyMatcher)parameters.get(0);
        ZYXImageKey key = param.getImageKey();

        // Now figure out the x, y values for this tile by getting the layer's
        // total bounding box
        // and figuring out the matrix width and height at the tile's zoom
        // level.

        long zoomLevel = key.getZ();
        TileDao dao;
        synchronized (myGeoPackage)
        {
            dao = myGeoPackage.getTileDao(layer);
        }

        long column = key.getX();
        long row = key.getY();

        TileRow tileRow = dao.queryForTile(column, row, zoomLevel);

        if (tileRow != null && tileRow.getTileData() != null)
        {
            InputStream imageData = myEncoder.encodeImage(tileRow.getTileData());

            DataModelCategory imageCategory = new DataModelCategory(category.getSource(), layer, Image.class.getName());
            Collection<PropertyAccessor<InputStream, ?>> imageAccessors = New.collection();
            imageAccessors.add(SerializableAccessor.<InputStream, String>getSingletonAccessor(
                    GeoPackagePropertyDescriptors.KEY_PROPERTY_DESCRIPTOR, param.getOperand()));
            imageAccessors.add(IMAGE_STREAM_ACCESSOR);

            DefaultCacheDeposit<InputStream> imageDeposit = new DefaultCacheDeposit<>(imageCategory, imageAccessors,
                    New.list(imageData), true, CacheDeposit.SESSION_END, false);

            try
            {
                queryReceiver.receive(imageDeposit);
            }
            catch (CacheException e)
            {
                throw new QueryException(e);
            }
        }
    }

    public  GeoPackageImageEnvoy(Toolbox toolbox, GeoPackage geoPackage);

}