Package org.geotools.mbtiles.mosaic

Source Code of org.geotools.mbtiles.mosaic.MBTilesReader

package org.geotools.mbtiles.mosaic;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.awt.image.Raster;
import java.awt.image.SampleModel;
import java.awt.image.WritableRaster;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;

import javax.imageio.ImageIO;
import javax.imageio.ImageReadParam;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;

import org.geotools.coverage.CoverageFactoryFinder;
import org.geotools.coverage.grid.GridCoverage2D;
import org.geotools.coverage.grid.GridCoverageFactory;
import org.geotools.coverage.grid.GridEnvelope2D;
import org.geotools.coverage.grid.GridGeometry2D;
import org.geotools.coverage.grid.io.AbstractGridCoverage2DReader;
import org.geotools.coverage.grid.io.AbstractGridFormat;
import org.geotools.factory.Hints;
import org.geotools.geometry.GeneralEnvelope;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.mbtiles.MBTilesFile;
import org.geotools.mbtiles.MBTilesMetadata;
import org.geotools.mbtiles.MBTilesTile;
import org.geotools.referencing.CRS;
import org.opengis.coverage.grid.Format;
import org.opengis.parameter.GeneralParameterValue;
import org.opengis.parameter.ParameterValue;
import org.opengis.referencing.ReferenceIdentifier;
import org.opengis.referencing.crs.CoordinateReferenceSystem;


public class MBTilesReader extends AbstractGridCoverage2DReader {
   
    final static CoordinateReferenceSystem SPHERICAL_MERCATOR;

    final static CoordinateReferenceSystem WGS_84;
   
    static {
        try {
            SPHERICAL_MERCATOR = CRS.decode("EPSG:3857", true);
            WGS_84 = CRS.decode("EPSG:4326", true);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
   
    protected final static ReferencedEnvelope WORLD_ENVELOPE = new ReferencedEnvelope(-20037508.34,20037508.34,-20037508.34,20037508.34, SPHERICAL_MERCATOR);
   
    protected final static int DEFAULT_TILE_SIZE = 256;
   
    protected final static int ZOOM_LEVEL_BASE = 2;
       
    protected GridCoverageFactory coverageFactory;
   
    protected MBTilesMetadata metadata;
   
    protected ReferencedEnvelope bounds;
   
    protected File sourceFile;
       
    public MBTilesReader(Object source, Hints hints) throws IOException {
        sourceFile = MBTilesFormat.getFileFromSource(source);

        MBTilesFile file = new MBTilesFile(sourceFile);

        metadata = file.loadMetaData();
       
        try {
            bounds = ReferencedEnvelope.create(metadata.getBounds(), WGS_84).transform(SPHERICAL_MERCATOR, true);
        } catch (Exception e) {
            bounds = null;
        }
        originalEnvelope = new GeneralEnvelope(bounds == null ? WORLD_ENVELOPE : bounds);

        long maxZoom;
        try {
            maxZoom = file.maxZoom();
        } catch (SQLException e) {
            throw new IOException(e);
        }

        long size = Math.round(Math.pow(ZOOM_LEVEL_BASE, maxZoom)) * DEFAULT_TILE_SIZE;

        highestRes = new double[] { WORLD_ENVELOPE.getSpan(0) / size, WORLD_ENVELOPE.getSpan(1) / size };

        originalGridRange = new GridEnvelope2D(new Rectangle((int) size, (int) size));

        coverageFactory = CoverageFactoryFinder.getGridCoverageFactory(this.hints);

        crs = SPHERICAL_MERCATOR;
    }

    @Override
    public Format getFormat() {
        return new MBTilesFormat();
    }
   
    @Override
    public GridCoverage2D read(GeneralParameterValue[] parameters) throws IllegalArgumentException, IOException {
        MBTilesFile file = new MBTilesFile(sourceFile);
       
        ReferencedEnvelope requestedEnvelope = null;
        Rectangle dim = null;
       
        if (parameters != null) {
            for (int i = 0; i < parameters.length; i++) {
                final ParameterValue param = (ParameterValue) parameters[i];
                final ReferenceIdentifier name = param.getDescriptor().getName();
                if (name.equals(AbstractGridFormat.READ_GRIDGEOMETRY2D.getName())) {
                    final GridGeometry2D gg = (GridGeometry2D) param.getValue();
                    try {                       
                        requestedEnvelope = ReferencedEnvelope.create(gg.getEnvelope(), gg.getCoordinateReferenceSystem()).transform(SPHERICAL_MERCATOR, true);;
                    } catch (Exception e) {
                        requestedEnvelope = null;
                    }
                   
                    dim = gg.getGridRange2D().getBounds();
                    continue;
                }
            }
        }
       
        if (requestedEnvelope == null) {
            requestedEnvelope = bounds;
        }   
       
        long zoomLevel = 0;
        long leftTile, topTile, rightTile, bottomTile;
       
        if (requestedEnvelope != null && dim != null) {
            //find the closest zoom based on horizontal resolution
            double ratioWidth = requestedEnvelope.getSpan(0) / WORLD_ENVELOPE.getSpan(0); //proportion of total width that is being requested
            double propWidth = dim.getWidth() / ratioWidth; //this is the width in pixels that the whole world would have in the requested resolution
            zoomLevel = Math.round(Math.log(propWidth / DEFAULT_TILE_SIZE) / Math.log(ZOOM_LEVEL_BASE));
            //the closest zoom level to the resolution, based on the formula width = zoom_base^zoom_level * tile_size -> zoom_level = log(width / tile_size)/log(zoom_base)
        }
                               
        try { //now take a zoom level that is available in the database
            zoomLevel = file.closestZoom(zoomLevel);
        } catch (SQLException e1) {
            throw new IOException(e1);
        }       
       
        long numberOfTiles = Math.round(Math.pow(ZOOM_LEVEL_BASE, zoomLevel)); //number of tile columns/rows for chosen zoom level
        double resX = WORLD_ENVELOPE.getSpan(0) / numberOfTiles; //points per tile
        double resY = WORLD_ENVELOPE.getSpan(1) / numberOfTiles; //points per tile       
        double offsetX = WORLD_ENVELOPE.getMinimum(0);
        double offsetY = WORLD_ENVELOPE.getMinimum(1);     
       
        try { //take available tiles from database
            leftTile = file.minColumn(zoomLevel);
            rightTile = file.maxColumn(zoomLevel);
            bottomTile = file.minRow(zoomLevel);
            topTile = file.maxRow(zoomLevel);           
        } catch (SQLException e) {
            throw new IOException(e);
        }
       
        if (requestedEnvelope != null) { //crop tiles to requested envelope                  
            leftTile = Math.max(leftTile, Math.round(Math.floor((requestedEnvelope.getMinimum(0) - offsetX) / resX )));
            bottomTile = Math.max(bottomTile, Math.round(Math.floor((requestedEnvelope.getMinimum(1) - offsetY) / resY )));
            rightTile = Math.max(leftTile, Math.min(rightTile, Math.round(Math.floor((requestedEnvelope.getMaximum(0) - offsetX) / resX ))));
            topTile = Math.max(bottomTile, Math.min(topTile, Math.round(Math.floor((requestedEnvelope.getMaximum(1) - offsetY) / resY ))));           
        }
       
        int width = (int) (rightTile - leftTile + 1) * DEFAULT_TILE_SIZE;
        int height = (int) (topTile - bottomTile + 1) * DEFAULT_TILE_SIZE;
       
        //recalculate the envelope we are actually returning
        ReferencedEnvelope resultEnvelope = new ReferencedEnvelope(offsetX + leftTile * resX, offsetX + (rightTile+1) * resX, offsetY + bottomTile * resY, offsetY + (topTile+1) * resY, SPHERICAL_MERCATOR);
                       
        BufferedImage image = null;
       
        MBTilesFile.TileIterator it;
        try {
          it = file.tiles(zoomLevel, leftTile, bottomTile, rightTile, topTile);
        } catch (SQLException e) {
            throw new IOException(e);
        }

        while (it.hasNext()) {               
            MBTilesTile tile = it.next();
           
            BufferedImage tileImage = readImage(tile.getData(), metadata.getFormatStr()==null? "png" : metadata.getFormatStr());
           
            if (image == null) {
                image = getStartImage(tileImage, width, height);
            }

            //coordinates
            int posx = (int) (tile.getTileColumn() - leftTile) * DEFAULT_TILE_SIZE;
            int posy = (int) (topTile - tile.getTileRow()) * DEFAULT_TILE_SIZE;

            image.getRaster().setRect(posx, posy, tileImage.getData() );
        }
       
        it.close();
               
        if (image == null){ // no tiles ??
            image = getStartImage(width, height);
        }
       
        return coverageFactory.create(metadata.getName()==null? "nameless mbtiles" : metadata.getName(), image, resultEnvelope);
    }
   
    protected static BufferedImage readImage(byte[] data, String format) throws IOException {
        ByteArrayInputStream bis = new ByteArrayInputStream(data);
        Iterator<?> readers = ImageIO.getImageReadersByFormatName(format);
        ImageReader reader = (ImageReader) readers.next();
        Object source = bis;
        ImageInputStream iis = ImageIO.createImageInputStream(source);
        reader.setInput(iis, true);
        ImageReadParam param = reader.getDefaultReadParam();
        return reader.read(0, param);
    }
   
    protected BufferedImage getStartImage(BufferedImage copyFrom, int width, int height) {
        Map<String, Object> properties = null;

        if (copyFrom.getPropertyNames() != null) {
            properties = new HashMap<String, Object>();
            for (String name : copyFrom.getPropertyNames()) {
                properties.put(name, copyFrom.getProperty(name));
            }
        }

        SampleModel sm = copyFrom.getSampleModel().createCompatibleSampleModel(width, height);
        WritableRaster raster = Raster.createWritableRaster(sm, null);

        BufferedImage image = new BufferedImage(copyFrom.getColorModel(), raster,
                copyFrom.isAlphaPremultiplied(), (Hashtable<?, ?>) properties);

        //white background
        Graphics2D g2D = (Graphics2D) image.getGraphics();
        Color save = g2D.getColor();
        g2D.setColor(Color.WHITE);
        g2D.fillRect(0, 0, image.getWidth(), image.getHeight());
        g2D.setColor(save);

        return image;
    }
   
    protected BufferedImage getStartImage(int imageType, int width, int height) {
        if (imageType == BufferedImage.TYPE_CUSTOM)
                imageType = BufferedImage.TYPE_3BYTE_BGR;

        BufferedImage image = new BufferedImage(width, height, imageType);

        //white background
        Graphics2D g2D = (Graphics2D) image.getGraphics();
        Color save = g2D.getColor();
        g2D.setColor(Color.WHITE);
        g2D.fillRect(0, 0, image.getWidth(), image.getHeight());
        g2D.setColor(save);

        return image;
    }
   
    protected BufferedImage getStartImage(int width, int height) {
        return getStartImage(BufferedImage.TYPE_CUSTOM, width, height);
    }
   

}
TOP

Related Classes of org.geotools.mbtiles.mosaic.MBTilesReader

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.