/* * Data HUb Service (DHuS) - For Space data distribution. * Copyright (C) 2013,2014,2015,2016 European Space Agency (ESA) * Copyright (C) 2013,2014,2015,2016 GAEL Systems * Copyright (C) 2013,2014,2015,2016 Serco Spa * * 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.drb.cortex.topic.sentinel2; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.log4j.Logger; /** * This class consists of static methods for <a href= * "http://www.esa.int/Our_Activities/Observing_the_Earth/Copernicus/Sentinel-2" * >Sentinel-2</a> Product ingestion. */ public class Sentinel2Utils { /** * A logger for this class. */ static private Logger logger = Logger.getLogger(Sentinel2Utils.class); /** * A data formater. */ static public final String DATE_TIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"; /** * ??? */ static public final long SENSING_TIME = 3608; // from document FOM MSI v7.2.1 /** * Default constructor: does nothing. */ private Sentinel2Utils() { // Does nothing } /** * Coordinates conversion. The template string of coordinates is: * <p> * "x1 y1 z1 x2 y2 z2 x3 y3 z3 ... x1 y1 z1" * </p> * <p> * The method returns the list of 2D coordinates following the pattern passed * as param. * </p> * * @param points * @param pattern * @param coordSeparator * @param pointSeparator * @return */ private static String processXYZPoints(final String points, final String pattern, final String coordSeparator, final String pointSeparator) { final int X_RELATIVE_INDEX = 0; final int Y_RELATIVE_INDEX = 1; final int COORDS_NUMBER = 3; final String POINT_SEPARATOR = " "; final String[] pointsArray = points.split(POINT_SEPARATOR); String coords = ""; logger.debug("(processXYZPoints)(points:" + points + ",pattern:" + pattern + ",coordSeparator:" + coordSeparator + ",pointSeparator:" + pointSeparator + ")|start"); try { for (int i = 0; i < pointsArray.length; i = i + COORDS_NUMBER) { coords += String.format( pattern, pointsArray[i + X_RELATIVE_INDEX], pointsArray[i + Y_RELATIVE_INDEX], coordSeparator, ((i != (pointsArray.length - COORDS_NUMBER)) ? pointSeparator : "")); } } catch (final Exception ex) { logger.warn("Sentinel-2 ingestion: Failed coordinates processing. " + "error message: " + ex.getMessage()); } logger.debug("(processXYZPoints) return:" + coords + "|end"); return coords; } /** * Convertion of Sentinel-2 footprint product coordinates (XYZ format) to <a * href="http://en.wikipedia.org/wiki/Geography_Markup_Language">Geography * Markup Language</a>. * <p> * GML example: * </p> * * <pre> * <gml:Polygon> * <gml:outerBoundaryIs> * <gml:LinearRing> * <gml:coordinates>0,0 100,0 100,100 0,100 0,0</gml:coordinates> * </gml:LinearRing> * </gml:outerBoundaryIs> * </gml:Polygon> * </pre> * * @param pointsString * @return */ public static String xYZpoints2GML(final String pointsString) { final String PATTERN = "%1$s%3$s%2$s%4$s"; final String COORD_SEPARATOR = ","; final String POINT_SEPARATOR = " "; logger.debug("(xYZpoints2GML)(pointsString:" + pointsString + ")|start"); final String result = Sentinel2Utils.processXYZPoints(pointsString, PATTERN, COORD_SEPARATOR, POINT_SEPARATOR); logger.debug("(xYZpoints2GML) return:" + result + "|end"); return result; } /** * Convertion of Sentinel-2 footprint product coordinates (XYZ format) to <a * href="http://en.wikipedia.org/wiki/JTS_Topology_Suite">Java Topology * Suite</a>. * <p> * JTS example: * </p> * * <pre> * POLYGON ((1368.62186660165 17722.3281808793, -1653 9287.5, 4038.14058906538 8613.02390521266, 1368.62186660165 17722.3281808793)) * </pre> * * @param pointsString * @return */ public static String xYZpoints2JTS(final String pointsString) { final String PATTERN = "%2$s%3$s%1$s%4$s"; final String COORD_SEPARATOR = " "; final String POINT_SEPARATOR = ","; logger.debug("(xYZpoints2JTS)(pointsString:" + pointsString + ")|start"); final String result = Sentinel2Utils.processXYZPoints(pointsString, PATTERN, COORD_SEPARATOR, POINT_SEPARATOR); logger.debug("(xYZpoints2JTS) return:" + result + "|end"); return result; } /** * Coordinates convertion The template string of coordinates is: * <p> * "x1 y1 x2 y2 x3 y3 ... x1 y1" * </p> * <p> * The method returns the list of 2D coordinates following the pattern passed * as param. * </p> * * @param points * @param pattern * @param coordSeparator * @param pointSeparator * @return */ private static String processXYPoints(final String points, final String pattern, final String coordSeparator, final String pointSeparator) { final int X_RELATIVE_INDEX = 0; final int Y_RELATIVE_INDEX = 1; final int COORDS_NUMBER = 2; final String[] pointsArray = points.split(" "); String coords = ""; logger.debug("(processXYPoints)(points:" + points + ",pattern:" + pattern + ",coordSeparator:" + coordSeparator + ",pointSeparator:" + pointSeparator + ")|start"); try { for (int i = 0; i < pointsArray.length; i = i + COORDS_NUMBER) { coords += String.format( pattern, pointsArray[i + X_RELATIVE_INDEX], pointsArray[i + Y_RELATIVE_INDEX], coordSeparator, ((i != (pointsArray.length - COORDS_NUMBER)) ? pointSeparator : "")); } } catch (final Exception ex) { logger.warn("Sentinel-2 ingestion: Failed footprint processing. error: " + ex.getMessage()); } logger.debug("(processXYPoints) return:" + coords + "|end"); return coords; } /** * Convertion of Sentinel-2 footprint product coordinates (XY format) to <a * href="http://en.wikipedia.org/wiki/Geography_Markup_Language">Geography * Markup Language</a>. * <p> * GML example: * </p> * * <pre> * <gml:Polygon> * <gml:outerBoundaryIs> * <gml:LinearRing> * <gml:coordinates>0,0 100,0 100,100 0,100 0,0</gml:coordinates> * </gml:LinearRing> * </gml:outerBoundaryIs> * </gml:Polygon> * </pre> * * @param pointsString * @return */ public static String xYPoints2GML(final String pointsString) { final String PATTERN = "%1$s%3$s%2$s%4$s"; final String COORD_SEPARATOR = ","; final String POINT_SEPARATOR = " "; logger.debug("(xYPoints2GML)(pointsString:" + pointsString + ")|start"); final String GMLPoints = Sentinel2Utils.processXYPoints(pointsString, PATTERN, COORD_SEPARATOR, POINT_SEPARATOR); logger.debug("(xYPoints2GML) return:" + GMLPoints + "|end"); return GMLPoints; } /** * Convertion of Sentinel-2 footprint product coordinates (XY format) to <a * href="http://en.wikipedia.org/wiki/JTS_Topology_Suite">Java Topology * Suite</a>. * <p> * JTS example: * </p> * * <pre> * POLYGON ((1368.62186660165 17722.3281808793, -1653 9287.5, * 4038.14058906538 8613.02390521266, * 1368.62186660165 17722.3281808793)) * </pre> * * @param pointsString * @return */ public static String xYPoints2JTS(final String pointsString) { final String PATTERN = "%2$s%3$s%1$s%4$s"; final String COORD_SEPARATOR = " "; final String POINT_SEPARATOR = ","; logger.debug("(xYPoints2JTS)(pointsString:" + pointsString + ")|start"); final String JTSPoints = Sentinel2Utils.processXYPoints(pointsString, PATTERN, COORD_SEPARATOR, POINT_SEPARATOR); logger.debug("(xYPoints2JTS) return:" + JTSPoints + "|end"); return JTSPoints; } /** * Generate end position from start position. * * @param startTime * @return */ public static String getEndPositionByStart(final String startTime) { long endTimeStamp = 0; final DateFormat formatter = new SimpleDateFormat(Sentinel2Utils.DATE_TIME_FORMAT); logger.debug("(getEndPositionByStart)(startTime:" + startTime + ")|start"); try { endTimeStamp = formatter.parse(startTime).getTime() + Sentinel2Utils.SENSING_TIME; } catch (final Exception ex) { logger.warn("Sentinel-2 ingestion: Cannot parse start sensing " + "time. error: " + ex.getMessage()); return ""; } final String result = formatter.format(endTimeStamp); logger.debug("(getEndPositionByStart) return:" + result + "|end"); return result; } /** * Get satellite name from product name * * @param productName * @return */ public static String getSatelliteByProductName(final String productName) { final String PATTERN = "S2[AB]"; String result = ""; logger.debug("(getSatelliteByProductName)(productName:" + productName + ")|start"); try { final Matcher matcher = Pattern.compile(PATTERN).matcher(productName); result = (matcher.find()) ? productName.substring(matcher.start(), matcher.end()) : ""; } catch (final Exception ex) { logger.warn("Sentinel-2 ingestion: Failed getting satellite name " + "by product."); } logger.debug("(getSatelliteByProductName) return:" + result + "|end"); return result; } /** * Remove useless zeros from string number * * @param integer * @return */ public static String filterUselessZeros(final String integer) { logger.debug("(filterUselessZeros)(integer:" + integer + ")|start"); // Remove useless zeroes final String result = new Integer(Integer.parseInt(integer)).toString(); logger.debug("(filterUselessZeros) return:" + result + "|end"); return result; } /** * Get Absolute orbit from Datatake file name * * @param datatakeFileName * @return */ public static String getAbsoluteOrbitFromDatatakeFilename( final String datatakeFileName) { final int START_ABSOLUTE_ORBIT_INDEX = 21; final int END_ABSOLUTE_ORBIT_INDEX = 27; String absoluteOrbitNumber = ""; logger.debug("(getAbsoluteOrbitFromDatatakeFilename)(datatakeFileName:" + datatakeFileName + ")|start"); if ((datatakeFileName != null) && !datatakeFileName.equals("")) { try { absoluteOrbitNumber = datatakeFileName.substring(START_ABSOLUTE_ORBIT_INDEX, END_ABSOLUTE_ORBIT_INDEX); } catch (final Exception ex) { logger.warn("Sentinel-2 ingestion: Datatake file name is empty; " + "error message: " + ex.getMessage()); } } final String result = Sentinel2Utils.filterUselessZeros(absoluteOrbitNumber); logger.debug("(getAbsoluteOrbitFromDatatakeFilename) return:" + result + "|end"); return result; } /** * Get dateTime string (with template: "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'" ) from * formatted time string in product file name * * @param rawTimeString * @return */ public static String getFormattedTimeString(final String rawTimeString) { Date date = null; String formattedDate = ""; final String RAW_TIME_STRING_TEMPLATE = "yyyyMMdd'T'HHmmss"; logger.debug("(getFormattedTimeString)(rawTimeString:" + rawTimeString + ")|start"); try { date = new SimpleDateFormat(RAW_TIME_STRING_TEMPLATE).parse(rawTimeString); } catch (final Exception ex) { logger.warn("Sentinel-2 ingestion: Date format wrong; error message: " + ex.getMessage()); } if (date != null) { formattedDate = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'").format(date); } logger.debug("(getFormattedTimeString) return:" + formattedDate + "|end"); return formattedDate; } /** * Format input number using the format string "%.2f" * * @param value * @return */ public static String formatNumber(double value){ logger.debug("----------Input value : "+value); return String.format("%.2f",value); } } // End Sentinel2Utils class