/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2016, Open Source Geospatial Foundation (OSGeo) * * 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.geotools.coverage.io.netcdf; import java.io.File; import java.io.FileFilter; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import java.io.StringWriter; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.logging.Logger; import javax.imageio.ImageReader; import javax.xml.transform.OutputKeys; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamSource; import org.apache.commons.io.FileUtils; import org.apache.commons.io.FilenameUtils; import org.apache.commons.io.filefilter.FileFilterUtils; import org.geotools.imageio.netcdf.NetCDFImageReaderSpi; import org.jdom.Document; import org.jdom.Element; import org.jdom.JDOMException; import org.jdom.input.SAXBuilder; import org.jdom.xpath.XPath; /** * CommandLine Utility to be used in order to create an ImageMosaic indexer.xml as well as * the related NetCDF low-level indexer (_auxiliary.xml) for a prototype NetCDF. * * Arguments to be provided are, in the following order: * " /path/to/sampleFile.nc " * " -p /path/to/netcdfprojectionsfile" * " [/path/to/optional/outputFolder]" * * @author Daniele Romagnoli, GeoSolutions SAS */ public class CreateIndexer { private static final String ELEVATION_ATTRIB_TYPE_FLOAT = "java.lang.Float"; private static final String ELEVATION_ATTRIB_TYPE_DOUBLE = "java.lang.Double"; private static final String TIME_ATTRIB_TYPE = "java.util.Date"; final static Logger LOGGER = org.geotools.util.logging.Logging.getLogger(CreateIndexer.class); public static void main(String[] args) throws JDOMException, IOException, TransformerException { if (args.length < 1) { System.out.println("Usage: java -jar CreateIndexer" + " /path/to/sampleFile.nc " + "/path/to/netcdfprojectionsfile " + "[/path/to/optional/outputFolder]\n" ); System.exit(1); } if (!args[1].equalsIgnoreCase("-P")) { System.out.println(" The second parameter needs to be " + "-p /path/to/netcdfprojectionsfile\n"); System.exit(1); } String sampleFilePath = args[0]; String projectionFilePath = args[2]; System.out.println("Setting netcdf.projections.file = " + projectionFilePath); System.setProperty("netcdf.projections.file", projectionFilePath); // Force the quickscan properties so that only 1 slice for NetCDF variable // will be used at time of NetCDF index creation. System.setProperty("org.geotools.netcdf.quickscan", "TRUE"); File sampleFile = new File(sampleFilePath); File temp = File.createTempFile("XML", "NC"); if(!(temp.delete())) { throw new IOException("Could not delete temp file: " + temp.getAbsolutePath()); } if(!(temp.mkdir())) { throw new IOException("Could not create temp directory: " + temp.getAbsolutePath()); } System.setProperty("NETCDF_DATA_DIR", temp.getAbsolutePath()); System.out.println("Reading sample file: " + sampleFilePath); ImageReader reader = new NetCDFImageReaderSpi().createReaderInstance(); reader.setInput(sampleFile); reader.dispose(); File[] files = temp.listFiles((FileFilter) FileFilterUtils.directoryFileFilter()); files = files[0].listFiles((FileFilter) FileFilterUtils.suffixFileFilter("xml")); final File auxiliaryFile = files[0]; String parentFolder = setOuputFolder(args, sampleFile); String indexerFilePath = parentFolder + File.separatorChar + "indexer.xml"; String auxiliaryFilePath = parentFolder + File.separatorChar + "_auxiliary.xml"; final File finalAuxFile = new File(auxiliaryFilePath); formatAuxiliaryXml(auxiliaryFile, finalAuxFile); System.out.println("Grabbing the generated xml: " + finalAuxFile); SAXBuilder saxBuilder = new SAXBuilder(); Document doc = saxBuilder.build(finalAuxFile); Element root = doc.getRootElement(); Set<String> timeAttributes = new HashSet<String>(); Set<String> elevationAttributes = new HashSet<String>(); getAttributes(timeAttributes, elevationAttributes, root); final StringBuilder builder = new StringBuilder("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); builder.append("<Indexer>\n"); setDomains(timeAttributes, elevationAttributes, builder); boolean longNameFound = setCoverages(root, builder); setParameters(auxiliaryFilePath, builder, longNameFound); writeIndexer(builder.toString(), indexerFilePath); System.out.println("Deleting temporary folder"); if(!(FileUtils.deleteQuietly(temp))) { System.out.println("Unable to delete folder: " + temp); } System.out.println("DONE!!"); } private static String setOuputFolder(String[] args, File sampleFile) { String outputPath = FilenameUtils.getFullPathNoEndSeparator(sampleFile.getAbsolutePath()); if (args.length > 3) { outputPath = args[3]; System.out.println("Output folder has been specified: " + outputPath); final File outputFolder = new File(outputPath); if (!outputFolder.exists()) { System.out.println("Creating it"); outputFolder.mkdirs(); } } else { System.out.println("Output folder hasn't been specified. The files will be created beside the sample file, at: " + outputPath); } return outputPath; } private static void writeIndexer(String xml, String indexerFilePath) throws FileNotFoundException { System.out.println("Writing the indexer.xml: " + indexerFilePath); PrintWriter out = new PrintWriter(indexerFilePath); out.println(xml); out.flush(); out.close(); } private static void formatAuxiliaryXml(File auxiliaryFile, File finalAuxFile) throws FileNotFoundException, TransformerException { Transformer transformer = TransformerFactory.newInstance().newTransformer(); transformer.setOutputProperty(OutputKeys.INDENT, "yes"); transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2"); //initialize StreamResult with File object to save to file StreamResult result = new StreamResult(new StringWriter()); InputStream is = null; PrintWriter out = null; try { is = new FileInputStream(auxiliaryFile); transformer.transform(new StreamSource(is), result); String xmlString = result.getWriter().toString(); out = new PrintWriter(finalAuxFile); out.println(xmlString); out.flush(); } finally { if (is != null) { try { is.close(); } catch (Exception e) { // does nothing } } if (out != null) { try { out.close(); } catch (Exception e) { // does nothing } } } } private static void setParameters(String auxiliaryFilePath, StringBuilder builder, boolean longNameFound) { builder.append(" <parameters>\n"); builder.append(" <parameter name=\"AuxiliaryFile\" value=\"" + auxiliaryFilePath + "\" />\n"); builder.append(" <parameter name=\"AbsolutePath\" value=\"true\" />\n"); if (longNameFound) { builder.append(" <parameter name=\"WrapStore\" value=\"true\" />\n"); } builder.append(" </parameters>\n"); builder.append("</Indexer>\n"); } private static boolean setCoverages(Element root, StringBuilder builder) throws JDOMException { builder.append(" <coverages>\n"); List<Element> coverages = XPath.selectNodes(root, "coverages/coverage"); boolean longName = false; for (Element cov : coverages) { if (setCoverage(cov, builder)) { longName = true; } } builder.append(" </coverages>\n"); return longName; } private static boolean setCoverage(Element cov, StringBuilder builder) throws JDOMException { builder.append(" <coverage>\n"); Element name = (Element) XPath.selectSingleNode(cov, "name"); String coverageName = name.getText(); builder.append(" <name>" + coverageName + "</name>\n"); Element schema = (Element) XPath.selectSingleNode(cov, "schema"); String schemaName = schema.getAttributeValue("name"); builder.append(" <schema name=\"" + schemaName + "\" >\n"); Element schemaAttributesElement = (Element) XPath .selectSingleNode(schema, "attributes"); String schemaAttribs = schemaAttributesElement.getText(); schemaAttribs = schemaAttribs.replace("imageindex:Integer", "imageindex:Integer,location:String"); builder.append(" <attributes>" + schemaAttribs + "</attributes>\n"); builder.append(" </schema>\n"); addDomainsToCoverage(schemaAttribs, builder); builder.append(" </coverage>\n"); return coverageName.length() > 62; } private static void addDomainsToCoverage(String schemaAttribs, StringBuilder builder) { Set<String> domains = new HashSet<String>(); String[] schemaAttributesValues = schemaAttribs.split(","); for (String schemaAttr : schemaAttributesValues) { if (schemaAttr.contains(TIME_ATTRIB_TYPE)) { String[] nameTypePair = schemaAttr.split(":"); domains.add(nameTypePair[0]); } if (schemaAttr.contains(ELEVATION_ATTRIB_TYPE_FLOAT) || schemaAttr.contains(ELEVATION_ATTRIB_TYPE_DOUBLE)) { String[] nameTypePair = schemaAttr.split(":"); domains.add(nameTypePair[0]); } } if (!domains.isEmpty()) { builder.append(" <domains>\n"); Iterator<String> it = domains.iterator(); while (it.hasNext()) { builder.append(" <domain ref=\"" + it.next() + "\" />\n"); } builder.append(" </domains>\n"); } } private static void getAttributes(Set<String> timeAttributes, Set<String> elevationAttributes, Element root) throws JDOMException { List<Element> schemaAttributes = XPath.selectNodes(root, "coverages/coverage/schema/attributes"); for (Element e : schemaAttributes) { String attributes = e.getText(); String[] attribs = attributes.split(","); for (String attrib : attribs) { if (attrib.contains(TIME_ATTRIB_TYPE)) { String[] nameTypePair = attrib.split(":"); String name = nameTypePair[0]; if (!timeAttributes.contains(name)) { timeAttributes.add(name); } } else if (attrib.contains(ELEVATION_ATTRIB_TYPE_FLOAT) || attrib.contains(ELEVATION_ATTRIB_TYPE_DOUBLE)) { String[] nameTypePair = attrib.split(":"); String name = nameTypePair[0]; if (!elevationAttributes.contains(name)) { elevationAttributes.add(name); } } } } } private static void setDomains(Set<String> timeAttributes, Set<String> elevationAttributes, StringBuilder builder) { builder.append(" <domains>\n"); for (String timeDomain : timeAttributes) { setDomain(builder, timeDomain); } for (String elevationDomain : elevationAttributes) { setDomain(builder, elevationDomain); } builder.append(" </domains>\n"); } private static void setDomain(StringBuilder builder, String domain) { builder.append(" <domain name=\"" + domain + "\">\n"); builder.append(" <attributes><attribute>" + domain + "</attribute></attributes>\n"); builder.append(" </domain>\n"); } }