/*
* Geotoolkit - An Open Source Java GIS Toolkit
* http://www.geotoolkit.org
*
* (C) 2012-2014, Geomatys
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
package org.geotoolkit.coverage.xmlstore;
import java.awt.*;
import java.awt.color.ColorSpace;
import java.awt.image.ColorModel;
import java.awt.image.IndexColorModel;
import java.awt.image.RenderedImage;
import java.awt.image.SampleModel;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import javax.swing.ProgressMonitor;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;
import org.apache.sis.storage.DataStoreException;
import org.apache.sis.util.ArgumentChecks;
import org.apache.sis.xml.MarshallerPool;
import org.geotoolkit.nio.IOUtilities;
import org.geotoolkit.storage.coverage.AbstractPyramidalCoverageReference;
import org.geotoolkit.storage.coverage.GridMosaic;
import org.geotoolkit.coverage.GridSampleDimension;
import org.geotoolkit.storage.coverage.Pyramid;
import org.geotoolkit.coverage.grid.ViewType;
import org.geotoolkit.coverage.io.CoverageStoreException;
import org.geotoolkit.util.NamesExt;
import org.opengis.coverage.SampleDimensionType;
import org.opengis.util.GenericName;
import org.geotoolkit.image.color.ScaledColorSpace;
import org.geotoolkit.image.internal.ImageUtils;
import org.geotoolkit.image.iterator.PixelIterator;
import org.geotoolkit.image.iterator.PixelIteratorFactory;
import org.geotoolkit.internal.jdk8.JDK8;
import org.opengis.geometry.DirectPosition;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.apache.sis.util.logging.Logging;
import org.geotoolkit.image.internal.PlanarConfiguration;
import org.geotoolkit.image.internal.SampleType;
import static java.nio.file.StandardOpenOption.CREATE;
import static java.nio.file.StandardOpenOption.TRUNCATE_EXISTING;
import static java.nio.file.StandardOpenOption.WRITE;
/**
* XML implementation of {@link PyramidalCoverageReference}.
*
* @author Johann Sorel (Geomatys)
* @author Remi Marechal (Geomatys)
* @module
*/
@XmlRootElement(name="CoverageReference")
public class XMLCoverageReference extends AbstractPyramidalCoverageReference {
/**
* Changes :
* 1.1 - number format used to name folder was using system local,
* local is fixed to EN in 1.1.
*/
private static final String CURRENT_VERSION = "1.1";
@XmlTransient
private static MarshallerPool POOL;
static {
try {
POOL = new MarshallerPool(JAXBContext.newInstance(XMLCoverageReference.class), null);
} catch (JAXBException ex) {
Logging.getLogger("org.geotoolkit.coverage.xmlstore").log(Level.WARNING, ex.getMessage(), ex);
throw new RuntimeException("Failed to initialize JAXB XML Coverage reference marshaller pool.");
}
}
private static final GenericName DEFAULT_NAME = NamesExt.create("default");
@XmlElement(name="Version")
private String version = CURRENT_VERSION;
@XmlElement(name="PyramidSet")
private XMLPyramidSet set;
/** One of geophysics/native */
@XmlElement(name="packMode")
private String packMode = ViewType.RENDERED.name();
@XmlElement(name="SampleDimension")
private List<XMLSampleDimension> sampleDimensions = null;
@XmlElement(name="NumDimension")
private int numDimension;
@XmlElement(name="PreferredFormat")
private String preferredFormat;
//-- needed element to define sampleModel of current pyramided tiles
/**
* Define band number of all internaly pyramid stored tiles.
*/
@XmlElement(name="NumBands")
private int nbBands = -1;
/**
* Define internal datatype of all internaly pyramid stored tiles.
*/
@XmlElement(name="SampleType")
private int sampleType = -1;
/**
* Define bits per sample number of all internaly pyramid stored tiles.
*/
@XmlElement(name="BitPerSample")
private int bitPerSample = -1;
/**
* Define sample number by pixels of all internaly pyramid stored tiles.
*/
@XmlElement(name="SamplePerPixel")
private int samplePerPixel = -1;
/**
* Define planar configuration of all internaly pyramid stored tiles.
*
* @see ImageUtils#PLANAR_BANDED
* @see ImageUtils#PLANAR_INTERLEAVED
*/
@XmlElement(name="PlanarConfiguration")
private int planarConfiguration = -1;
/**
* Define sample format of all internaly pyramid stored tiles.
*
* @see ImageUtils#SAMPLEFORMAT_IEEEFP
* @see ImageUtils#SAMPLEFORMAT_INT
* @see ImageUtils#SAMPLEFORMAT_UINT
*/
@XmlElement(name="SampleFormat")
private int sampleFormat = -1;
/**
* Define photometric interpretation of all internaly pyramid stored tiles.
*
* @see ImageUtils#PHOTOMETRIC_MINISBLACK
* @see ImageUtils#PHOTOMETRIC_PALETTE
* @see ImageUtils#PHOTOMETRIC_RGB
*/
@XmlElement(name="PhotometricInterpretation")
private int photometricInterpretation = -1;
/**
* Define color map of all internaly pyramid stored tiles.
* Only use if photometric interpretation is use as {@linkplain ImageUtils#PHOTOMETRIC_PALETTE palette}.
*
* @see ImageUtils#PHOTOMETRIC_PALETTE
*/
@XmlElement(name="ColorMap")
private int[] colorMap = null;
/**
* Minimum sample value only use in case where {@link ColorSpace} from {@link #colorModel}
* is instance of {@link ScaledColorSpace} and type of samples are Float or double type.
* @see ScaledColorSpace
*/
@XmlElement(name="MinColorSpaceSampleValue")
private Double minColorSampleValue = null;
/**
* Maximum sample value only use in case where {@link ColorSpace} from {@link #colorModel}
* is instance of {@link ScaledColorSpace} and type of samples are Float or double type.
* @see ScaledColorSpace
*/
@XmlElement(name="MaxColorSpaceSampleValue")
private Double maxColorSampleValue = null;
/**
* {@link SampleModel} of all internally pyramid stored tiles.
*/
private SampleModel sampleModel;
/**
* {@link ColorModel} of all internally pyramid stored tiles.
*/
private ColorModel colorModel;
private String id;
private Path mainfile;
//caches
private List<GridSampleDimension> cacheDimensions = null;
public XMLCoverageReference() {
super(null, DEFAULT_NAME, 0);
}
public XMLCoverageReference(XMLCoverageStore store, GenericName name, XMLPyramidSet set) {
super(store,name,0);
this.set = set;
this.set.setRef(this);
}
public List<XMLSampleDimension> getXMLSampleDimensions() {
if (sampleDimensions == null) sampleDimensions = new ArrayList<>();
return sampleDimensions;
}
public void copy(XMLCoverageReference ref){
this.version = ref.version;
this.id = ref.id;
this.mainfile = ref.mainfile;
this.set = ref.set;
this.packMode = ref.packMode;
this.sampleDimensions = ref.sampleDimensions;
this.preferredFormat = ref.preferredFormat;
this.nbBands = ref.nbBands;
// this.sampleType = ref.sampleType;
this.bitPerSample = ref.bitPerSample;
// this.samplePerPixel = ref.samplePerPixel;
this.planarConfiguration = ref.planarConfiguration;
this.sampleFormat = ref.sampleFormat;
this.photometricInterpretation = ref.photometricInterpretation;
this.colorMap = ref.colorMap;
this.minColorSampleValue = ref.minColorSampleValue;
this.maxColorSampleValue = ref.maxColorSampleValue;
this.set.setRef(this);
}
void initialize(Path mainFile) throws DataStoreException {
this.mainfile = mainFile;
//calculate id based on file name
id = IOUtilities.filenameWithoutExtension(mainFile);
// In case we created the reference by unmarshalling a file, the pyramid set is not bound to its parent coverage reference.
final XMLPyramidSet set = getPyramidSet();
if (set.getRef() == null) {
set.setRef(this);
}
for (XMLPyramid pyramid : set.pyramids()) {
pyramid.initialize(set);
}
}
public String getVersion() {
if(version==null) version = CURRENT_VERSION;
return version;
}
public void setVersion(String version) {
this.version = version;
}
public String getId() {
return id;
}
/**
* @return xml file where the pyramid set definition is stored.
*/
public Path getMainfile() {
return mainfile;
}
/**
* @return Folder where each pyramid is stored.
*/
public Path getFolder(){
return mainfile.getParent().resolve(getId());
}
@Override
public XMLPyramidSet getPyramidSet() {
return set;
}
private void checkColorModel(){
if (minColorSampleValue == null) {
assert maxColorSampleValue == null;
if (colorModel != null)
assert !(colorModel.getColorSpace() instanceof ScaledColorSpace) : "with min and max NULL sample value color space should not be instance of ScaledColorSpace.";
} else {
assert maxColorSampleValue != null;
assert (colorModel.getColorSpace() instanceof ScaledColorSpace) : "with NOT NULL min and max sample value color space must be instance of ScaledColorSpace.";
assert JDK8.isFinite(minColorSampleValue) : "To write minColorSampleValue into XML Pyramid File, it should be finite. Found : "+minColorSampleValue;
assert JDK8.isFinite(maxColorSampleValue) : "To write maxColorSampleValue into XML Pyramid File, it should be finite. Found : "+maxColorSampleValue;
}
}
/**
* Save the coverage reference in the file
* @throws DataStoreException
*/
private final AtomicInteger save = new AtomicInteger(0b00);
void save() throws DataStoreException {
//unecessary here, values are checked when setting colormodel and updating tile
//checkColorModel();
/*
The save atomic integer contains 2 bits.
0b01 : is a flag for 'save is needed'
0b10 : is a flag for 'someone has taken the charge of saving'
This is an optimized opportunist saving approach.
One thread might do more then one saving work but this avoid a global
contention when multiple save operations occur.
*/
if((save.getAndSet(0b11) & 0b10) == 0){
//0b10 flag was not set, no thread was currently saving so we must take this role.
while(updateAndGet(save)!=0){
//keep saving un 0b01 flag is set to zero
try (OutputStream os = Files.newOutputStream(getMainfile(), CREATE, WRITE, TRUNCATE_EXISTING)) {
final Marshaller marshaller = POOL.acquireMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
marshaller.marshal(this, os);
POOL.recycle(marshaller);
} catch (JAXBException ex) {
Logging.getLogger("org.geotoolkit.coverage.xmlstore").log(Level.WARNING, ex.getMessage(), ex);
} catch (IOException e) {
throw new DataStoreException("Unable to save pyramid definition : "+e.getLocalizedMessage(), e);
}
}
}
}
/**
* Modified backport copy from JDK8 AtomicInteger.updateAndGet
*/
private static final int updateAndGet(AtomicInteger ati) {
int prev, next;
do {
prev = ati.get();
next = prev==0b11 ? 0b10 : 0b00;
} while (!ati.compareAndSet(prev, next));
return next;
}
/**
* Read the given file and return an XMLCoverageReference.
*
* @param file
* @return
* @throws JAXBException if an error occured while reading descriptor file.
* @throws org.apache.sis.storage.DataStoreException if the file describe a pyramid, but it contains an invalid CRS.
*/
@Deprecated
public static XMLCoverageReference read(File file) throws JAXBException, DataStoreException {
final Unmarshaller unmarshaller = POOL.acquireUnmarshaller();
final XMLCoverageReference ref;
ref = (XMLCoverageReference) unmarshaller.unmarshal(file);
POOL.recycle(unmarshaller);
ref.initialize(file.toPath());
return ref;
}
/**
* Read the given Path and return an XMLCoverageReference.
*
* @param file
* @return
* @throws JAXBException if an error occurred while reading descriptor file.
* @throws org.apache.sis.storage.DataStoreException if the file describe a pyramid, but it contains an invalid CRS.
*/
public static XMLCoverageReference read(Path file) throws JAXBException, DataStoreException, IOException {
final Unmarshaller unmarshaller = POOL.acquireUnmarshaller();
final XMLCoverageReference ref;
try (InputStream is = Files.newInputStream(file)) {
ref = (XMLCoverageReference) unmarshaller.unmarshal(is);
POOL.recycle(unmarshaller);
ref.initialize(file);
}
return ref;
}
////////////////////////////////////////////////////////////////////////////
// Meta informations methods ///////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
public void setPreferredFormat(String preferredFormat) {
this.preferredFormat = preferredFormat;
}
public String getPreferredFormat() {
return preferredFormat;
}
/**
* {@inheritDoc }.
*/
@Override
public synchronized List<GridSampleDimension> getSampleDimensions() throws DataStoreException {
if (cacheDimensions == null) {
if (sampleDimensions == null) return null;
assert !sampleDimensions.isEmpty() : "XmlCoverageReference.getSampleDimension : sampleDimension should not be empty.";
cacheDimensions = new CopyOnWriteArrayList<>();
for (XMLSampleDimension xsd : sampleDimensions) {
cacheDimensions.add(xsd.buildSampleDimension());
}
}
return cacheDimensions;
}
/**
* {@inheritDoc }.
*/
@Override
public void setSampleDimensions(List<GridSampleDimension> dimensions) throws DataStoreException {
if (dimensions == null || dimensions.isEmpty()) return;
this.cacheDimensions = null; //clear cache
if (sampleDimensions == null) sampleDimensions = new CopyOnWriteArrayList<>();
sampleDimensions.clear();
for (GridSampleDimension dimension : dimensions) {
dimension = dimension.geophysics(false);
final SampleDimensionType sdt = dimension.getSampleDimensionType();
final XMLSampleDimension dim = new XMLSampleDimension();
dim.fill(dimension);
dim.setSampleType(sdt);
sampleDimensions.add(dim);
}
assert !sampleDimensions.isEmpty() : "XmlCoverageReference.setSampleDimension : sampleDimension should not be empty.";
save();
}
/**
* Returns {@link ColorModel} associated with internal pyramid data.
*
* Note : if internal {@link ColorModel} is {@code null}, the expected sample
* model is re-built from internal unmarshalling informations which are :<br>
* - {@linkplain #bitPerSample bit Per Sample}<br>
* - {@linkplain #nbBands band number}<br>
* - {@linkplain #samplePerPixel sample Per Pixel}<br>
* - {@linkplain #planarConfiguration planar configuration}<br>
* - {@linkplain #photometricInterpretation photometric interpretation}<br>
* - {@linkplain #sampleFormat sample format}<br><br>
*
* Moreover should return {@code null} when old pyramid was unmarshall, where
* internal color model informations was missing.
*
* @return {@link ColorModel} associated with internal pyramid data.
* @throws IllegalArgumentException if photometric interpretation is define
* as palette (photometricInterpretation == 3) and colorMap is {@code null}.
* @see ImageUtils#createColorModel(int, int, short, short, long[])
*/
@Override
public ColorModel getColorModel() {
if (colorModel == null) {
if (checkAttributs()) {
if (minColorSampleValue == null) {
assert maxColorSampleValue == null;
if (colorModel != null)
assert !(colorModel.getColorSpace() instanceof ScaledColorSpace) : "with min and max NULL sample value color space should not be instance of ScaledColorSpace.";
} else {
assert maxColorSampleValue != null;
assert (colorModel.getColorSpace() instanceof ScaledColorSpace) : "with NOT NULL min and max sample value color space must be instance of ScaledColorSpace.";
assert JDK8.isFinite(minColorSampleValue) : "To write minColorSampleValue into XML Pyramid File, it should be finite. Found : "+minColorSampleValue;
assert JDK8.isFinite(maxColorSampleValue) : "To write maxColorSampleValue into XML Pyramid File, it should be finite. Found : "+maxColorSampleValue;
}
colorModel = ImageUtils.createColorModel(bitPerSample, nbBands,
(short) photometricInterpretation, (short) sampleFormat,
minColorSampleValue, maxColorSampleValue, colorMap);
}
}
return colorModel;
}
/**
* Set the associate internal pyramid data {@link ColorModel}.<br>
*
* Note : also set some attributes needed to marshall / unmarshall.
*
* @param colorModel {@code ColorModel} internal data.
* @throws DataStoreException
* @see #photometricInterpretation
*/
@Override
public void setColorModel(ColorModel colorModel) throws DataStoreException {
ArgumentChecks.ensureNonNull("colorModel", colorModel);
//--photometric
this.photometricInterpretation = ImageUtils.getPhotometricInterpretation(colorModel);
if (photometricInterpretation == 3) {
assert colorModel instanceof IndexColorModel : "with photometric interpretation "
+ "define as palette color model should be instance of IndexColorModel.";
final IndexColorModel indexColorMod = ((IndexColorModel) colorModel);
int mapSize = indexColorMod.getMapSize();
colorMap = new int[mapSize];
indexColorMod.getRGBs(colorMap);
}
this.colorModel = colorModel;
checkColorModel();
//-- code in comment in attempt to update TiffImageReader to scan all sampleValues to build appropriate colorSpace
// final ColorSpace colorSpace = colorModel.getColorSpace();
// if (colorSpace instanceof ScaledColorSpace) {
// minColorSampleValue = (minColorSampleValue == null)
// ? colorSpace.getMinValue(0)
// : StrictMath.min(minColorSampleValue, colorSpace.getMinValue(0));
// maxColorSampleValue = (maxColorSampleValue == null)
// ? colorSpace.getMaxValue(0)
// : StrictMath.max(maxColorSampleValue, colorSpace.getMaxValue(0));
//
// }
save();
}
/**
* Returns {@link SampleModel} associated with internal pyramid data.
*
* Note : if internal {@link SampleModel} is {@code null}, the expected sample
* model is re-built from internal unmarshalling informations which are :<br>
* - {@linkplain #bitPerSample bit Per Sample}<br>
* - {@linkplain #nbBands band number}<br>
* - {@linkplain #samplePerPixel sample Per Pixel}<br>
* - {@linkplain #planarConfiguration planar configuration}<br>
* - {@linkplain #photometricInterpretation photometric interpretation}<br>
* - {@linkplain #sampleFormat sample format}<br><br>
*
* Moreover should return {@code null} when old pyramid was unmarshall, where
* internal sample model informations was missing.
*
* @return {@link SampleModel} associated with internal pyramid data.
* @see ImageUtils#buildImageTypeSpecifier(int, int, short, short, short, long[])
*/
@Override
public SampleModel getSampleModel() {
if (sampleModel == null) {
final ColorModel colMod = getColorModel();
if (colMod != null && planarConfiguration != -1) {
//-- we can directly create sample model, attributs have already been checked.
sampleModel = ImageUtils.createSampleModel(PlanarConfiguration.valueOf(planarConfiguration),
SampleType.valueOf(bitPerSample, sampleFormat),
1, 1, nbBands);
}
//-- else do nothing case where unmarshall old pyramid. older comportement.
}
return sampleModel;
}
/**
* Returns {@code true} if all needed attributs to re-build appropriate
* {@link SampleModel} and {@link ColorModel} else return {@code false}.
*
* @return
*/
private boolean checkAttributs() {
return (bitPerSample != -1)
&& (nbBands != -1)
// && (samplePerPixel != -1)
&& (sampleFormat != -1)
&& (planarConfiguration != -1)
&& (photometricInterpretation != -1);
}
/**
* Set the associate internal pyramid data {@link SampleModel}.<br>
* note : also set some attributs needed to marshall/unmarshall pyramid.
*
* @param sampleModel expected {@link SampleModel} needed to marshall.
* @throws org.apache.sis.storage.DataStoreException if problem during XML save.
* @see #nbBands
* @see #sampleType
* @see #samplePerPixel
*/
@Override
public void setSampleModel(SampleModel sampleModel) throws DataStoreException {
// this.sampleType = sampleModel.getDataType();
this.nbBands = sampleModel.getNumBands();
sampleFormat = ImageUtils.getSampleFormat(sampleModel);
final int[] bitPerSample = sampleModel.getSampleSize();
// samplePerPixel = bitPerSample.length;
assert bitPerSample.length == nbBands;
this.bitPerSample = bitPerSample[0];
planarConfiguration = ImageUtils.getPlanarConfiguration(sampleModel);
this.sampleModel = sampleModel;
save();
}
/**
* Verify conformity between a {@link SampleModel} and {@link ColorModel} given by a tile which WILL BE stored
* into this pyramid and the already pyramid setted sampleModel.
*
* If they haven't got any {@link SampleModel} and {@link ColorModel} precedently setted,
* this method set them automaticaly, from image parameter.
*
* @param image {@link RenderedImage} which contain samplemodel color model informations.
*/
private void checkOrSetSampleColor(final RenderedImage image) throws DataStoreException {
final SampleModel imgSm = image.getSampleModel();
final SampleModel sm = getSampleModel();
if (sm != null) {
// if (imgSm.getDataType() != sampleType)
// throw new IllegalArgumentException(String.format("Mismatch sample type. Expected : %d .Found : %d", sampleType, imgSm.getDataType()));
if (imgSm.getNumBands() != nbBands)
throw new IllegalArgumentException(String.format("Mismatch bands number. Expected : %d .Found : %d", nbBands, imgSm.getNumBands()));
if (ImageUtils.getSampleFormat(imgSm) != sampleFormat)
throw new IllegalArgumentException(String.format("Mismatch sample format. Expected : %d .Found : %d", sampleFormat, ImageUtils.getSampleFormat(imgSm)));
if (imgSm.getSampleSize()[0] != bitPerSample)
throw new IllegalArgumentException(String.format("Mismatch sample size (bits per samples). Expected : %d .Found : %d", bitPerSample, imgSm.getSampleSize()[0]));
// if (imgSm.getSampleSize().length != samplePerPixel)
// throw new IllegalArgumentException(String.format("Mismatch sample per pixel. Expected : %d .Found : %d", samplePerPixel, imgSm.getSampleSize().length));
if (ImageUtils.getPlanarConfiguration(imgSm) != planarConfiguration)
throw new IllegalArgumentException(String.format("Mismatch planar configuration. Expected : %d .Found : %d", planarConfiguration, ImageUtils.getPlanarConfiguration(imgSm)));
} else {
setSampleModel(imgSm);
}
final ColorModel cm = getColorModel();
final ColorModel imgCm = image.getColorModel();
//-- each tile may have different min and max value in its internal colorspace but it must be same type class.
final ColorSpace imgCmCS = imgCm.getColorSpace();
if (imgCmCS instanceof ScaledColorSpace) {
if (cm != null && (!(cm.getColorSpace() instanceof ScaledColorSpace)))
throw new IllegalArgumentException(String.format("Mismatch color space."));
if (minColorSampleValue == null) minColorSampleValue = Double.POSITIVE_INFINITY;
if (maxColorSampleValue == null) maxColorSampleValue = Double.NEGATIVE_INFINITY;
//-- when largeRenderedImage will own its right ScaledColorSpace
//-- moreover to have right ScaledColorSpace tiff reader must travel all of sample values.
//-- discomment following coding row
// {
// minColorSampleValue = StrictMath.min(minColorSampleValue, imgCmCS.getMinValue(0));
// maxColorSampleValue = StrictMath.max(maxColorSampleValue, imgCmCS.getMaxValue(0));
// }
//-- now to be in accordance with ScaledColorSpace properties
//-- travel all current image to find minimum and maximum raster values
final PixelIterator pix = PixelIteratorFactory.createDefaultIterator(image);
while (pix.next()) {
//-- to avoid unexpected NAN values comportement don't use StrictMath class.
final double value = pix.getSampleDouble();
if (value < minColorSampleValue) {
minColorSampleValue = value;
} else if (value > maxColorSampleValue) {
maxColorSampleValue = value;
}
}
//-- to refresh min and max of current stored color model
this.colorModel = null; //-- see : getColorModel()
}
if (this.colorModel != null) {
if (ImageUtils.getPhotometricInterpretation(this.colorModel) != photometricInterpretation)
throw new IllegalArgumentException(String.format("Mismatch photometric interpretation. Expected : %d .Found : %d", photometricInterpretation, ImageUtils.getPhotometricInterpretation(cm)));
} else {
setColorModel(imgCm);
}
}
/**
* {@inheritDoc }.
*/
@Override
public ViewType getPackMode() {
return ViewType.valueOf(packMode);
}
/**
* {@inheritDoc }.
*/
@Override
public void setPackMode(ViewType packMode) {
this.packMode = packMode.name();
}
////////////////////////////////////////////////////////////////////////////
// Creation,Edition methods ////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
/**
* {@inheritDoc }.
*/
@Override
public boolean isWritable() throws CoverageStoreException {
return true;
}
/**
* {@inheritDoc }.
*/
@Override
public Pyramid createPyramid(CoordinateReferenceSystem crs) throws DataStoreException {
final XMLPyramidSet set = getPyramidSet();
final Pyramid pyramid = set.createPyramid(getName().tip().toString(),crs);
save();
return pyramid;
}
/**
* {@inheritDoc }.
*/
@Override
public void deletePyramid(String pyramidId) throws DataStoreException {
throw new DataStoreException("Not supported yet.");
}
/**
* {@inheritDoc }.
*/
@Override
public GridMosaic createMosaic(String pyramidId, Dimension gridSize,
Dimension tilePixelSize, DirectPosition upperleft, double pixelscale) throws DataStoreException {
final XMLPyramidSet set = getPyramidSet();
final XMLPyramid pyramid = (XMLPyramid) set.getPyramid(pyramidId);
final XMLMosaic mosaic = pyramid.createMosaic(gridSize, tilePixelSize, upperleft, pixelscale);
save();
return mosaic;
}
/**
* {@inheritDoc }.
*/
@Override
public void deleteMosaic(String pyramidId, String mosaicId) throws DataStoreException {
throw new DataStoreException("Not supported yet.");
}
public Runnable acquireTileWriter(final BlockingQueue<XMLTileWriter.XMLTileInfo> tilesToWrite) {
return new XMLTileWriter(tilesToWrite, this);
}
/**
* {@inheritDoc }.
* @throws java.lang.IllegalArgumentException if mismatch in image DataType or image band number.
*/
@Override
public void writeTile(String pyramidId, String mosaicId, int col, int row, RenderedImage image) throws DataStoreException {
final XMLPyramidSet set = getPyramidSet();
final XMLPyramid pyramid = (XMLPyramid) set.getPyramid(pyramidId);
final XMLMosaic mosaic = pyramid.getMosaic(mosaicId);
//-- check conformity between internal data and currentImage.
//-- if no sm and cm automatical set from image (use for the first insertion)
checkOrSetSampleColor(image);
mosaic.createTile(col,row,image);
if (!mosaic.cacheTileState && mosaic.tileExist != null) {
save();
}
}
/**
* {@inheritDoc }.
*/
@Override
public void writeTiles(final String pyramidId, final String mosaicId, final RenderedImage image, final Rectangle area,
final boolean onlyMissing, final ProgressMonitor monitor) throws DataStoreException {
final XMLPyramidSet set = getPyramidSet();
final XMLPyramid pyramid = (XMLPyramid) set.getPyramid(pyramidId);
final XMLMosaic mosaic = pyramid.getMosaic(mosaicId);
//-- check conformity between internal data and currentImage.
//-- if no sm and cm automatical set from image (use for the first insertion)
checkOrSetSampleColor(image);
mosaic.writeTiles(image, area, onlyMissing, monitor);
if (!mosaic.cacheTileState && mosaic.tileExist != null) {
save();
}
}
/**
* {@inheritDoc }.
*/
@Override
public void deleteTile(String pyramidId, String mosaicId, int col, int row) throws DataStoreException {
throw new DataStoreException("Not supported yet.");
}
}