/* * Data Hub Service (DHuS) - For Space data distribution. * Copyright (C) 2013,2014,2015,2016 GAEL Systems * * This file is part of DHuS software sources. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package fr.gael.dhus.datastore.processing; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.StringReader; import java.net.URL; import java.util.ArrayList; import java.util.List; import javax.activation.MimeType; import javax.activation.MimeTypeParseException; import javax.media.jai.Interpolation; import javax.media.jai.JAI; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.LogManager; import org.geotools.gml2.GMLConfiguration; import org.geotools.xml.Configuration; import org.geotools.xml.Parser; import org.xml.sax.InputSource; import com.hp.hpl.jena.ontology.OntClass; import com.hp.hpl.jena.reasoner.IllegalParameterException; import com.hp.hpl.jena.util.iterator.ExtendedIterator; import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.io.WKTReader; import com.vividsolutions.jts.operation.valid.IsValidOp; import com.vividsolutions.jts.operation.valid.TopologyValidationError; import fr.gael.dhus.database.object.MetadataIndex; import fr.gael.dhus.database.object.Product; import fr.gael.dhus.util.UnZip; import fr.gael.drb.DrbAttribute; import fr.gael.drb.DrbFactory; import fr.gael.drb.DrbNode; import fr.gael.drb.DrbSequence; import fr.gael.drb.impl.xml.XmlWriter; import fr.gael.drb.query.Query; import fr.gael.drb.value.Value; import fr.gael.drbx.cortex.DrbCortexItemClass; import fr.gael.drbx.cortex.DrbCortexModel; /** * @author pidancier * */ public class ProcessingUtils { private static final Logger LOGGER = LogManager.getLogger(ProcessingUtils.class); /** * Hide utility class constructor */ private ProcessingUtils () { // Call only static methods. Constructor call forbidden. } public static RenderedImage resizeImage(RenderedImage image, int width, int height) throws InconsistentImageScale { RenderedImage resizedImage=image; // Computes ratio and scale float scale=getScale(image.getWidth(),image.getHeight(),width,height); // Processing resize process ParameterBlock pb = new ParameterBlock(); // The source image pb.addSource(resizedImage); // The xScale pb.add(scale); // The yScale pb.add(scale); // The x translation pb.add(0.0F); // The y translation pb.add(0.0F); // The interpolation pb.add(Interpolation.getInstance(Interpolation.INTERP_BICUBIC)); resizedImage = JAI.create("scale", pb, null); LOGGER.debug("Image resized to : " + resizedImage.getWidth() + "x" + resizedImage.getHeight()); return resizedImage; } /** * Computes the image scale keeping the aspect ratio. * The image size is never always exactly matching the target image size. * To manage all the possible different ratio, the algorithm of conservative * number of pixels is used: * When the image target is expected 512x512, it number of pixels is 262144 * if the source image is 15100x1217 computed image is 1803x145 * * @param orig_width source width. * @param orig_height source height. * @param width destination width. * @param height destination height. * @return */ static float getScale(int orig_width, int orig_height, int width, int height) throws InconsistentImageScale { float orig_ratio=(float)orig_width/orig_height; float target_pix_number = (float)width*height; double target_height = Math.sqrt(target_pix_number/orig_ratio); double target_width = target_height * orig_ratio; if ((target_height < 1) || (target_width) < 1) { throw new InconsistentImageScale( String.format("Wrong image scale (%.2f,%.2f)", target_width, target_height)); } float scale = (float)(target_height/orig_height); return scale; } public static DrbNode getNodeFromPath (String location) { DrbNode node = null; try { Query query = new Query(location); DrbSequence sequence = query.evaluate(DrbFactory.openURI(".")); if ((sequence == null) || (sequence.getLength() == 0)) return null; node = (DrbNode)sequence.getItem(0); } catch (Exception e) { DrbSequence sequence = null; String path = location.replace ('\\', '/'); String[] tokens = path.split ("/"); path = ""; for (int i=0; i<tokens.length; i++) { path += checkPathElement(tokens[i]) + "/"; LOGGER.debug("looking for path " + path); try { Query query = new Query(path); sequence = query.evaluate(DrbFactory.openURI(".")); } catch (Exception exc) { LOGGER.debug(path + " NOT supported by Drb."); } } if ((sequence == null) || (sequence.getLength() == 0)) return null; node = (DrbNode)sequence.getItem(0); } if (UnZip.supported (location)) { return node.getFirstChild (); } return node; } static String checkPathElement (String elt) { if (elt.matches ("(\\d.*)|(.*-.*)")) return "*[name()=\""+elt.replaceAll ("%20", " ")+"\"]"; else return elt; } /** * Retrieve DrbCortex class from passed node. * @param node the node to retrieve the class. * @return the related class to the node. * @throws IOException if model is not reachable. * @throws UnsupportedOperationException if class cannot be found. */ public static DrbCortexItemClass getClassFromNode(DrbNode node) throws IOException { DrbCortexModel model = DrbCortexModel.getDefaultModel (); DrbCortexItemClass cl = model.getClassOf (node); if(cl == null) { throw new UnsupportedOperationException( "Class cannot be retrieved for product " + node.getPath()); } LOGGER.info("Class \"" + cl.getLabel () + "\" for product " + node.getName ()); return cl; } /** * Retrieve DrbCortex class from passed url. * @param url the url to retrieve the class. * @return the related class to the url. * @throws IOException if model is not reachable. * @throws UnsupportedOperationException if class cannot be found. */ public static DrbCortexItemClass getClassFromUrl(URL url) throws IOException { return ProcessingUtils.getClassFromNode( ProcessingUtils.getNodeFromPath (url.getPath ())); } /** * Retrieve DrbCortex class from passed product. * @param url the product to retrieve the class. * @return the related class to the product. * @throws IOException if model is not reachable. * @throws UnsupportedOperationException if class cannot be found. */ public static DrbCortexItemClass getClassFromProduct(Product product) throws IOException { return ProcessingUtils.getClassFromUrl(product.getPath()); } public static List<String>getSuperClass(String URI) { List<String>super_classes = new ArrayList<String>(); DrbCortexItemClass cl = DrbCortexItemClass.getCortexItemClassByName(URI); ExtendedIterator it = cl.getOntClass().listSuperClasses(true); while (it.hasNext()) { String ns = ((OntClass)it.next()).getURI(); if(ns!=null) super_classes.add(ns); } return super_classes; } public static List<String>getSubClass(String URI) { List<String>sub_classes = new ArrayList<String>(); DrbCortexItemClass cl = DrbCortexItemClass.getCortexItemClassByName(URI); ExtendedIterator it = cl.getOntClass().listSubClasses(true); while (it.hasNext()) { String ns = ((OntClass)it.next()).getURI(); if(ns!=null) sub_classes.add(ns); } return sub_classes; } public static String getItemClassUri (DrbCortexItemClass cl) { return cl.getOntClass().getURI(); } public static List<String> getAllClasses () { List<String>classes = new ArrayList<String>(); DrbCortexModel model; try { model = DrbCortexModel.getDefaultModel(); } catch (IOException e) { return classes; } ExtendedIterator it= model.getCortexModel().getOntModel().listClasses(); while (it.hasNext()) { OntClass cl = (OntClass)it.next(); String uri = cl.getURI(); if (uri!=null) classes.add(uri); } return classes; } /** * Check GML Footprint validity */ public static boolean checkGMLFootprint (String footprint) { try { Configuration configuration = new GMLConfiguration (); Parser parser = new Parser (configuration); Geometry geom = (Geometry) parser.parse (new InputSource ( new StringReader (footprint))); if (!geom.isEmpty() && !geom.isValid()) { LOGGER.error("Wrong footprint"); return false; } } catch (Exception e) { LOGGER.error("Error in extracted footprint: " + e.getMessage()); return false; } return true; } /** * Check JTS Footprint validity */ public static boolean checkJTSFootprint (String footprint) { try { WKTReader wkt = new WKTReader(); Geometry geom = wkt.read(footprint); IsValidOp vaildOp = new IsValidOp(geom); TopologyValidationError err = vaildOp.getValidationError(); if (err != null) { throw new IllegalParameterException(err.getMessage()); } return true; } catch (Exception e) { LOGGER.error("JTS Footprint error : " + e.getMessage()); return false; } } private static final String METADATA_NAMESPACE = "http://www.gael.fr/dhus#"; private final static String MIME_PLAIN_TEXT = "plain/text"; private final static String PROPERTY_METADATA_EXTRACTOR = "metadataExtractor"; private final static String MIME_APPLICATION_GML = "application/gml+xml"; public static List<MetadataIndex> getIndexesFrom (URL url) { java.util.Collection<String> properties = null; DrbNode node = null; DrbCortexItemClass cl = null; // Prepare the index structure. List<MetadataIndex> indexes = new ArrayList<MetadataIndex> (); // Prepare the DRb node to be processed try { // First : force loading the model before accessing items. node = ProcessingUtils.getNodeFromPath (url.getPath ()); cl = ProcessingUtils.getClassFromNode (node); LOGGER.info("Class \"" + cl.getLabel () + "\" for product " + node.getName ()); // Get all values of the metadata properties attached to the item // class or any of its super-classes properties = cl.listPropertyStrings (METADATA_NAMESPACE + PROPERTY_METADATA_EXTRACTOR, false); // Return immediately if no property value were found if (properties == null) { LOGGER.warn("Item \"" + cl.getLabel () + "\" has no metadata defined."); return null; } } catch (IOException e) { throw new UnsupportedOperationException ( "Error While decoding drb node", e); } // Loop among retrieved property values for (String property : properties) { // Filter possible XML markup brackets that could have been encoded // in a CDATA section property = property.replaceAll ("<", "<"); property = property.replaceAll (">", ">"); /* * property = property.replaceAll("\n", " "); // Replace eol by blank * space property = property.replaceAll(" +", " "); // Remove * contiguous blank spaces */ // Create a query for the current metadata extractor Query metadataQuery = new Query (property); // Evaluate the XQuery DrbSequence metadataSequence = metadataQuery.evaluate (node); // Check that something results from the evaluation: jump to next // value otherwise if ( (metadataSequence == null) || (metadataSequence.getLength () < 1)) { continue; } // Loop among results for (int iitem = 0; iitem < metadataSequence.getLength (); iitem++) { // Get current metadata node DrbNode n = (DrbNode) metadataSequence.getItem (iitem); // Get name DrbAttribute name_att = n.getAttribute ("name"); Value name_v = null; if (name_att != null) name_v = name_att.getValue (); String name = null; if (name_v != null) name = name_v.convertTo (Value.STRING_ID).toString (); // get type DrbAttribute type_att = n.getAttribute ("type"); Value type_v = null; if (type_att != null) type_v = type_att.getValue (); else type_v = new fr.gael.drb.value.String (MIME_PLAIN_TEXT); String type = type_v.convertTo (Value.STRING_ID).toString (); // get category DrbAttribute cat_att = n.getAttribute ("category"); Value cat_v = null; if (cat_att != null) cat_v = cat_att.getValue (); else cat_v = new fr.gael.drb.value.String ("product"); String category = cat_v.convertTo (Value.STRING_ID).toString (); // get category DrbAttribute qry_att = n.getAttribute ("queryable"); String queryable = null; if (qry_att != null) { Value qry_v = qry_att.getValue (); if (qry_v != null) queryable = qry_v.convertTo (Value.STRING_ID).toString (); } // Get value String value = null; if (MIME_APPLICATION_GML.equals (type) && n.hasChild ()) { ByteArrayOutputStream out = new ByteArrayOutputStream (); XmlWriter.writeXML (n.getFirstChild (), out); value = out.toString (); try { out.close (); } catch (IOException e) { LOGGER.warn("Cannot close stream !", e); } } else // Case of "text/plain" { Value value_v = n.getValue (); if (value_v != null) { value = value_v.convertTo (Value.STRING_ID).toString (); value = value.trim (); } } if ( (name != null) && (value != null)) { MetadataIndex index = new MetadataIndex (); index.setName (name); try { index.setType (new MimeType (type).toString ()); } catch (MimeTypeParseException e) { LOGGER.warn( "Wrong metatdata extractor mime type in class \"" + cl.getLabel () + "\" for metadata called \"" + name + "\".", e); } index.setCategory (category); index.setValue (value); index.setQueryable (queryable); indexes.add (index); } else { String field_name = ""; if (name != null) field_name = name; else if (queryable != null) field_name = queryable; else if (category != null) field_name = "of category " + category; LOGGER.warn("Nothing extracted for field " + field_name); } } } return indexes; } }