/*
* Geotoolkit - An Open Source Java GIS Toolkit
* http://www.geotoolkit.org
*
* (C) 2012, 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.Dimension;
import java.io.IOException;
import java.io.Serializable;
import java.nio.file.Path;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.logging.Level;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;
import net.iharder.Base64;
import org.apache.sis.geometry.GeneralEnvelope;
import org.apache.sis.io.wkt.*;
import org.apache.sis.io.wkt.WKTFormat;
import org.apache.sis.storage.DataStoreException;
import org.apache.sis.util.ArgumentChecks;
import org.apache.sis.util.Classes;
import org.apache.sis.util.logging.Logging;
import org.geotoolkit.storage.coverage.GridMosaic;
import org.geotoolkit.storage.coverage.Pyramid;
import org.geotoolkit.gui.swing.tree.Trees;
import org.apache.sis.referencing.CRS;
import org.apache.sis.referencing.IdentifiedObjects;
import org.opengis.geometry.DirectPosition;
import org.opengis.geometry.Envelope;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
/**
*
* @author Johann Sorel (Geomatys)
* @module
*/
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name="XMLPyramid")
public class XMLPyramid implements Pyramid {
@XmlElement(name="id")
String id;
@XmlElement(name="crs")
String crs;
@XmlElement(name="serializedCrs")
String serializedCrs;
@XmlElement(name="Mosaic")
List<XMLMosaic> mosaics = new CopyOnWriteArrayList<>();
@XmlTransient
private XMLPyramidSet set;
@XmlTransient
protected CoordinateReferenceSystem crsobj;
/**
* Default constructor is reserved for JAXB usage. If an user wants to create a pyramid, he MUST initialize it with
* a CRS, using following builder : {@linkplain #XMLPyramid(org.opengis.referencing.crs.CoordinateReferenceSystem)}.
*/
private XMLPyramid() {}
XMLPyramid(CoordinateReferenceSystem pyramidCRS) throws DataStoreException {
setCoordinateReferenceSystem(pyramidCRS);
}
/**
* Initialize the pyramid, including its inner mosaics.
* @param set The parent set of pyramid for this object.
* @throws DataStoreException If the pyramid CRS is null or invalid.
*/
void initialize(XMLPyramidSet set) throws DataStoreException {
// A simple security. If the pyramid CRS is invalid, we will return the error at initialization.
try {
getCoordinateReferenceSystem();
} catch (Exception e) {
throw new DataStoreException("Pyramid cannot be initialized : invalid CRS.", e);
}
this.set = set;
for(XMLMosaic mosaic : mosaics()){
mosaic.initialize(this);
}
}
public List<XMLMosaic> mosaics() {
if(mosaics == null){
mosaics = new CopyOnWriteArrayList<>();
}
return mosaics;
}
@Override
public String getId() {
return id;
}
public Path getFolder(){
return getPyramidSet().getRef().getFolder().resolve(getId());
}
@Override
public XMLPyramidSet getPyramidSet() {
return set;
}
@Override
public CoordinateReferenceSystem getCoordinateReferenceSystem() {
if(crsobj != null){
return crsobj;
}
if (serializedCrs != null) {
try {
crsobj = (CoordinateReferenceSystem) Base64.decodeToObject(serializedCrs);
} catch (Exception ex) {
final String msg = "Unable to read base64 serialized CRS, fallback to WKT : "+ex.getMessage();
Logging.getLogger("org.geotoolkit.coverage.xmlstore").log(Level.WARNING, msg);
}
}
if (crs != null && crsobj == null) {
try {
if (crs.startsWith("EPSG")) {
crsobj = CRS.forCode(crs);
} else {
crsobj = CRS.fromWKT(crs);
}
} catch (Exception e) {
// Exception should be thrown only at initialisation, as we will call the method at object built.
throw new RuntimeException(e);
}
}
return crsobj;
}
void setCoordinateReferenceSystem(CoordinateReferenceSystem crs) throws DataStoreException {
ArgumentChecks.ensureNonNull("Input CRS", crs);
//-- init
crsobj = crs;
this.crs = null;
this.serializedCrs = null;
//-- try wkt2 writing
final WKTFormat f = new WKTFormat(null, null);
f.setConvention(Convention.WKT2);
this.crs = f.format(crs);
if (crs instanceof Serializable) {
try {
this.serializedCrs = Base64.encodeObject((Serializable)crs);
} catch (IOException serializedEx) {
Logging.getLogger("org.geotoolkit.coverage.xmlstore").log(Level.WARNING, serializedEx.getMessage(), serializedEx);
}
}
////// //-- if problem try to serialize CRS
////// if (f.getWarnings() != null) {
////// if (crs instanceof Serializable) {
////// try {
////// this.serializedCrs = Base64.encodeObject((Serializable)crs);
////// } catch (IOException serializedEx) {
////// throw new DataStoreException(serializedEx);
////// }
////// }
////// }
assert (this.crs != null || serializedCrs != null);
}
@Override
public double[] getScales() {
final SortedSet<Double> scaleSet = new TreeSet<Double>();
for(GridMosaic m : mosaics()){
scaleSet.add(m.getScale());
}
final double[] scales = new double[scaleSet.size()];
int i=0;
for(Double d : scaleSet){
scales[i] = d;
i++;
}
return scales;
}
@Override
public Collection<GridMosaic> getMosaics(int index) {
final List<GridMosaic> candidates = new ArrayList<GridMosaic>();
final double[] scales = getScales();
for(GridMosaic m : mosaics()){
if(m.getScale() == scales[index]){
candidates.add(m);
}
}
return candidates;
}
@Override
public List<GridMosaic> getMosaics() {
return new ArrayList<GridMosaic>(mosaics());
}
@Override
public Envelope getEnvelope() {
GeneralEnvelope env = null;
for(GridMosaic mosaic : getMosaics()){
if(env==null){
env = new GeneralEnvelope(mosaic.getEnvelope());
}else{
env.add(mosaic.getEnvelope());
}
}
return env;
}
@Override
public String toString(){
return Trees.toString(
Classes.getShortClassName(this)
+" "+IdentifiedObjects.getIdentifierOrName(getCoordinateReferenceSystem())
+" "+getId(),
mosaics());
}
/**
* Return the mosaic with the given id
*
* @param mosaicId
* @return
* @throws DataStoreException if no mosaic have this id
*/
XMLMosaic getMosaic(String mosaicId) throws DataStoreException{
for(final XMLMosaic mo : mosaics()){
if(mosaicId.equals(mo.getId())){
return mo;
}
}
throw new DataStoreException("No mosaic for ID : " + mosaicId);
}
/**
* Create and register a new mosaic in this pyramid
*
* @param gridSize
* @param tilePixelSize
* @param upperleft
* @param pixelscale
* @return
*/
XMLMosaic createMosaic(Dimension gridSize, Dimension tilePixelSize, DirectPosition upperleft, double pixelscale) {
final XMLMosaic mosaic = new XMLMosaic();
mosaic.scale = pixelscale;
mosaic.gridWidth = gridSize.width;
mosaic.gridHeight = gridSize.height;
mosaic.tileWidth = tilePixelSize.width;
mosaic.tileHeight = tilePixelSize.height;
mosaic.upperLeft = upperleft.getCoordinate();
mosaics.add(mosaic);
mosaic.initialize(this);
return mosaic;
}
}