Advertisement
aghoshpro

translateGeoToGridCoorinates

Jun 25th, 2023
99
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 32.97 KB | None | 0 0
  1. /*
  2.  * This file is part of rasdaman community.
  3.  *
  4.  * Rasdaman community is free software: you can redistribute it and/or modify
  5.  * it under the terms of the GNU General Public License as published by
  6.  * the Free Software Foundation, either version 3 of the License, or
  7.  * (at your option) any later version.
  8.  *
  9.  * Rasdaman community is distributed in the hope that it will be useful,
  10.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  12.  * See the GNU  General Public License for more details.
  13.  *
  14.  * You should have received a copy of the GNU  General Public License
  15.  * along with rasdaman community.  If not, see <http://www.gnu.org/licenses/>.
  16.  *
  17.  * Copyright 2003 - 2018 Peter Baumann / rasdaman GmbH.
  18.  *
  19.  * For more information please see <http://www.rasdaman.org>
  20.  * or contact Peter Baumann via <[email protected]>.
  21.  */
  22. package petascope.wcps.handler;
  23.  
  24. import java.math.BigDecimal;
  25. import java.util.ArrayList;
  26. import java.util.List;
  27. import java.util.Map;
  28. import javax.servlet.http.HttpServletRequest;
  29. import com.rasdaman.accesscontrol.service.AuthenticationService;
  30. import org.springframework.beans.factory.annotation.Autowired;
  31. import petascope.core.BoundingBox;
  32. import petascope.core.Pair;
  33. import petascope.exceptions.ExceptionCode;
  34. import petascope.exceptions.PetascopeException;
  35. import petascope.util.CrsUtil;
  36. import petascope.util.ListUtil;
  37. import static petascope.util.ras.RasConstants.RASQL_BOUND_SEPARATION;
  38. import static petascope.util.ras.RasConstants.RASQL_CLOSE_SUBSETS;
  39. import static petascope.util.ras.RasConstants.RASQL_OPEN_SUBSETS;
  40. import petascope.util.ras.RasUtil;
  41. import petascope.wcps.exception.processing.WcpsRasqlException;
  42. import static petascope.wcps.handler.AbstractOperatorHandler.checkOperandIsCoverage;
  43. import petascope.wcps.metadata.model.Axis;
  44. import petascope.wcps.metadata.model.NumericSlicing;
  45. import petascope.wcps.metadata.model.NumericSubset;
  46. import petascope.wcps.metadata.model.NumericTrimming;
  47. import petascope.wcps.metadata.model.ParsedSubset;
  48. import petascope.wcps.metadata.model.Subset;
  49. import petascope.wcps.metadata.model.WcpsCoverageMetadata;
  50. import petascope.wcps.metadata.service.CoordinateTranslationService;
  51. import petascope.wcps.metadata.service.CoverageAliasRegistry;
  52. import petascope.wcps.metadata.service.SubsetParsingService;
  53. import petascope.wcps.metadata.service.WcpsCoverageMetadataGeneralService;
  54. import petascope.wcps.result.WcpsResult;
  55. import petascope.wcps.subset_axis.model.AbstractWKTShape;
  56. import petascope.wcps.subset_axis.model.WKTCompoundPoint;
  57. import petascope.wcps.subset_axis.model.WKTCompoundPoints;
  58. import petascope.wcps.subset_axis.model.WcpsSliceSubsetDimension;
  59. import petascope.wcps.subset_axis.model.WcpsSubsetDimension;
  60. import petascope.wcps.subset_axis.model.WcpsTrimSubsetDimension;
  61.  
  62. /**
  63.  * Abstract class for all clip() epxression handlers.
  64.  *
  65.  * @author <a href="mailto:[email protected]">Bang Pham Huu</a>
  66.  */
  67. public abstract class AbstractClipExpressionHandler extends Handler {
  68.    
  69.     @Autowired
  70.     protected HttpServletRequest httpServletRequest;
  71.    
  72.     @Autowired
  73.     protected WcpsCoverageMetadataGeneralService wcpsCoverageMetadataGeneralService;
  74.     @Autowired
  75.     protected SubsetParsingService subsetParsingService;
  76.     @Autowired
  77.     protected CoverageAliasRegistry coverageAliasRegistry;
  78.     @Autowired
  79.     CoordinateTranslationService coordinateTranslationService;
  80.  
  81.     public static final String OPERATOR = "clip";
  82.    
  83.     protected static final String TRANSLATED_COVERAGE_EXPRESSION_RASQL_TEMPLATE = "$TRANSLATED_COVERAGE_EXPRESSION_RASQL";
  84.     protected static final String TRANSLATED_WKT_EXPRESSION_RASQL_TEMPLATE = "$TRANSLATED_WKT_EXPRESSION_RASQL";
  85.  
  86.      /**
  87.      * Convert a geoCoordinate for an axis to a numeric BigDecimal value
  88.      * (especially for DateTime coordinate).
  89.      *
  90.      * @param axis axis containing input coordinate
  91.      * @param geoCoordinate coordinate in string
  92.      * @return BigDecimal numeric geo coordinate
  93.      */
  94.     protected BigDecimal getNumericGeoCoordinate(WcpsCoverageMetadata metadata, Axis axis, String geoCoordinate) throws PetascopeException {
  95.         WcpsSubsetDimension wcpsSubsetDimension = new WcpsSliceSubsetDimension(axis.getLabel(), axis.getNativeCrsUri(), geoCoordinate);
  96.         List<WcpsSubsetDimension> wcpsSubsetDimensions = new ArrayList<>();
  97.         wcpsSubsetDimensions.add(wcpsSubsetDimension);
  98.         List<Subset> subsets = this.subsetParsingService.convertToNumericSubsets(wcpsSubsetDimensions, metadata.getAxes());
  99.         // this can never throw an exception, because the list is not empty
  100.         BigDecimal numericGeoCoordinate = subsets.get(0).getNumericSubset().getLowerLimit();
  101.  
  102.         return numericGeoCoordinate;
  103.     }
  104.  
  105.     /**
  106.      * Translate a geoPointCoordinate of an axis to grid pixel coordinate.
  107.      *
  108.      * @return a grid coordinate
  109.      * @throws PetascopeException
  110.      */
  111.     protected String translateGeoToGridPointCoordinate(Axis axis, BigDecimal geoPointCoordinate) throws PetascopeException {
  112.         ParsedSubset<BigDecimal> parsedSubset = new ParsedSubset<>(geoPointCoordinate, geoPointCoordinate);
  113.        
  114.         WcpsSubsetDimension subsetDimension = new WcpsTrimSubsetDimension(axis.getLabel(), axis.getNativeCrsUri(),
  115.                                                                          geoPointCoordinate.toPlainString(), geoPointCoordinate.toPlainString());
  116.        
  117.         // Translate geo coordinate of a slice point to a grid point
  118.         ParsedSubset<Long> gridSubset = wcpsCoverageMetadataGeneralService.translateGeoToGridCoordinates(subsetDimension, parsedSubset, axis,
  119.                 axis.getGeoBounds().getLowerLimit(),
  120.                 axis.getGeoBounds().getUpperLimit(),
  121.                 axis.getGridBounds().getLowerLimit(),
  122.                 axis.getGridBounds().getUpperLimit());
  123.         String gridPoint = gridSubset.getLowerLimit().toString();
  124.  
  125.         return gridPoint;
  126.     }
  127.  
  128.     /**
  129.      * The clipped output coverage needs to have new geo bounds after it is
  130.      * clipped by WKT shape.
  131.      *
  132.      * @param index the axis index to update bound
  133.      * @param bound the numeric geo value to be consider to be update as min/max
  134.      * of current axis's geo domain.
  135.      */
  136.     private void updateGeoBoundsClippedOutput( Map<String, Pair<BigDecimal, BigDecimal>> clippedCoverageAxesGeoBounds, int totalAxes, String axisName, BigDecimal bound) {
  137.         if (clippedCoverageAxesGeoBounds.size() < totalAxes) {
  138.             clippedCoverageAxesGeoBounds.put(axisName, new Pair<>(bound, bound));
  139.         } else {
  140.             Pair<BigDecimal, BigDecimal> pair = clippedCoverageAxesGeoBounds.get(axisName);
  141.             BigDecimal newLowerBound = pair.fst;
  142.             BigDecimal newUpperBound = pair.snd;
  143.             // lowerBound of axis
  144.             if (newLowerBound.compareTo(bound) > 0) {
  145.                 newLowerBound = bound;
  146.             } else if (newUpperBound.compareTo(bound) < 0) {
  147.                 newUpperBound = bound;
  148.             }
  149.             clippedCoverageAxesGeoBounds.put(axisName, new Pair<>(newLowerBound, newUpperBound));
  150.         }
  151.     }
  152.  
  153.     /**
  154.      * Get the axis's order in list of axis names.
  155.      */
  156.     private int getAxisOrder(List<String> axisNames, String name) throws PetascopeException {
  157.         int i = 0;
  158.        
  159.         for (String axisName : axisNames) {
  160.             if (CrsUtil.axisLabelsMatch(axisName, name)) {
  161.                 return i;
  162.             }
  163.            
  164.             i++;
  165.         }
  166.        
  167.         return -1;
  168.     }
  169.    
  170.     /**
  171.      * Check if petascope needs to sawp XY coordinates from input WKT
  172.      */
  173.     private boolean needToSwapXYCoordinates(String wktCRS, String axisXNativeCRS) throws PetascopeException {
  174.        return (wktCRS != null && !CrsUtil.isGridCrs(wktCRS) &&
  175.                 ( (CrsUtil.isXYAxesOrder(wktCRS) && !CrsUtil.isXYAxesOrder(axisXNativeCRS))
  176.                   || (!CrsUtil.isXYAxesOrder(wktCRS) && CrsUtil.isXYAxesOrder(axisXNativeCRS) )
  177.                 )
  178.             );            
  179.     }
  180.    
  181.     /**
  182.      * For example if WKT is a polygon over XY axes, then return the rectangle which contains this polygon
  183.      * and get geo min/max for XY axes.
  184.      */
  185.     private BoundingBox getConvexHullXYBoundingBox(WcpsCoverageMetadata metadata,
  186.                                                    String[] geoPoints, List<String> axisNames, String wktCRS) throws PetascopeException {
  187.         if (!metadata.hasXYAxes()) {
  188.             return null;
  189.         }
  190.         List<Axis> axes = metadata.getXYAxes();
  191.         Axis axisX = axes.get(0);
  192.         Axis axisY = axes.get(1);
  193.         int geoCoordinateXOrder = this.getAxisOrder(axisNames, axisX.getLabel());
  194.         int geoCoordinateYOrder = this.getAxisOrder(axisNames, axisY.getLabel());
  195.        
  196.         if (geoCoordinateXOrder == -1 || geoCoordinateYOrder == -1) {
  197.             return null;
  198.         }
  199.        
  200.         BigDecimal xmin = null, ymin = null, xmax = null, ymax = null;        
  201.        
  202.         for (String geoPoint : geoPoints) {
  203.             // e.g: 10 20
  204.             String[] geoCoordinateArray = geoPoint.split(" ");
  205.            
  206.             BigDecimal geoCoordinateX = null;
  207.             BigDecimal geoCoordinateY = null;
  208.             String valueX = geoCoordinateArray[geoCoordinateXOrder];
  209.             String valueY = geoCoordinateArray[geoCoordinateYOrder];
  210.            
  211.             if (needToSwapXYCoordinates(wktCRS, axisX.getNativeCrsUri())) {
  212.                 String tmp = valueX;
  213.                 valueX = valueY;
  214.                 valueY = tmp;
  215.             }
  216.            
  217.             try {
  218.                 geoCoordinateX = new BigDecimal(valueX);
  219.             } catch (Exception ex) {
  220.                 throw new PetascopeException(ExceptionCode.InvalidRequest, "Coordinate of X axis is not valid number for clipping. Given: " + valueX);
  221.             }
  222.            
  223.             try {
  224.                 geoCoordinateY = new BigDecimal(valueY);
  225.             } catch (Exception ex) {
  226.                 throw new PetascopeException(ExceptionCode.InvalidRequest, "Coordinate of Y axis is not valid number for clipping. Given: " + valueY);
  227.             }
  228.            
  229.             if (xmin == null) {
  230.                 xmin = geoCoordinateX;
  231.                 xmax = geoCoordinateX;
  232.                
  233.                 ymin = geoCoordinateY;
  234.                 ymax = geoCoordinateY;
  235.             } else {
  236.                 if (geoCoordinateX.compareTo(xmin) < 0) {
  237.                     xmin = new BigDecimal(geoCoordinateX.toPlainString());
  238.                 }
  239.                 if (geoCoordinateX.compareTo(xmax) > 0) {
  240.                     xmax = new BigDecimal(geoCoordinateX.toPlainString());
  241.                 }
  242.                
  243.                 if (geoCoordinateY.compareTo(ymin) < 0) {
  244.                     ymin = new BigDecimal(geoCoordinateY.toPlainString());
  245.                 }
  246.                 if (geoCoordinateY.compareTo(ymax) > 0) {
  247.                     ymax = new BigDecimal(geoCoordinateY.toPlainString());
  248.                 }
  249.             }
  250.         }
  251.        
  252.         if (wktCRS != null) {
  253.             // Reproject the bbox from input CRS to native CRS of coverage
  254.             Subset subsetX = new Subset(new NumericTrimming(xmin, xmax), wktCRS, axisX.getLabel());
  255.             Subset subsetY = new Subset(new NumericTrimming(ymin, ymax), wktCRS, axisY.getLabel());
  256.  
  257.             List<Subset> subsets = new ArrayList<>();
  258.             subsets.add(subsetX);
  259.             subsets.add(subsetY);
  260.            
  261.             this.wcpsCoverageMetadataGeneralService.transformSubsettingCrsXYSubsets(metadata, subsets);
  262.            
  263.             xmin = subsets.get(0).getNumericSubset().getLowerLimit();
  264.             xmax = subsets.get(0).getNumericSubset().getUpperLimit();
  265.            
  266.             ymin = subsets.get(1).getNumericSubset().getLowerLimit();
  267.             ymax = subsets.get(1).getNumericSubset().getUpperLimit();
  268.         }
  269.        
  270.        
  271.         return new BoundingBox(xmin, ymin, xmax, ymax);
  272.     }
  273.    
  274.     /**
  275.      * Translate the XY geo coordinates as slicing coordinates in clip() operator correctly
  276.      * based on the convex hull of input WKT and calculated result of subset trimming
  277.      * from XY min and max of convex hull.
  278.      */
  279.     private Pair<String, String> calculateXYGridBounds(Map<String, Pair<BigDecimal, BigDecimal>> clippedCoverageAxesGeoBounds,
  280.                                                      WcpsCoverageMetadata metadata,
  281.                                                      String[] geoCoordinateArray,
  282.                                                      List<Subset> subsets,
  283.                                                      BoundingBox convexHullXYBBox) throws PetascopeException {
  284.         // The bbox of this one is wrong
  285.         Axis axisX = metadata.getXYAxes().get(0);
  286.         Axis axisY = metadata.getXYAxes().get(1);
  287.         BigDecimal newGeoCoordinateX = subsets.get(0).getNumericSubset().getLowerLimit();
  288.         BigDecimal newGeoCoordinateY = subsets.get(1).getNumericSubset().getLowerLimit();
  289.  
  290.         // Update the geo bounds for clipped output coverage
  291.         if (metadata.isXYOrder()) {
  292.             // XY Axes order                    
  293.             this.updateGeoBoundsClippedOutput(clippedCoverageAxesGeoBounds, geoCoordinateArray.length, axisX.getLabel(), newGeoCoordinateX);
  294.             this.updateGeoBoundsClippedOutput(clippedCoverageAxesGeoBounds, geoCoordinateArray.length, axisY.getLabel(), newGeoCoordinateY);
  295.         } else {
  296.             // YX Axes order
  297.             this.updateGeoBoundsClippedOutput(clippedCoverageAxesGeoBounds, geoCoordinateArray.length, axisY.getLabel(), newGeoCoordinateY);
  298.             this.updateGeoBoundsClippedOutput(clippedCoverageAxesGeoBounds, geoCoordinateArray.length, axisX.getLabel(), newGeoCoordinateX);
  299.         }
  300.  
  301.         Pair<BoundingBox, BoundingBox> bboxesPair = this.coordinateTranslationService.calculateGridGeoXYBoundingBoxes(true, true, axisX, axisY, convexHullXYBBox);
  302.         BoundingBox adjustedConvexHullGeoXYBBox = bboxesPair.fst;
  303.         BoundingBox adjustedConvexHullGridXYBBox = bboxesPair.snd;
  304.  
  305.         BigDecimal lowerBoundX = newGeoCoordinateX;
  306.         BigDecimal upperBoundX = lowerBoundX;
  307.         BigDecimal lowerBoundY = newGeoCoordinateY;
  308.         BigDecimal upperBoundY = lowerBoundY;
  309.  
  310.         if (lowerBoundX.compareTo(adjustedConvexHullGeoXYBBox.getXMin()) < 0) {
  311.             lowerBoundX = adjustedConvexHullGeoXYBBox.getXMin();
  312.         }
  313.  
  314.         if (lowerBoundX.compareTo(adjustedConvexHullGeoXYBBox.getXMax()) > 0) {
  315.             lowerBoundX = adjustedConvexHullGeoXYBBox.getXMax();
  316.         }
  317.  
  318.         if (lowerBoundX.compareTo(upperBoundX) > 0) {
  319.             upperBoundX = lowerBoundX;
  320.         }
  321.  
  322.         if (lowerBoundY.compareTo(adjustedConvexHullGeoXYBBox.getYMin()) < 0) {
  323.             lowerBoundY = adjustedConvexHullGeoXYBBox.getYMin();
  324.         }
  325.  
  326.         if (lowerBoundY.compareTo(adjustedConvexHullGeoXYBBox.getYMax()) > 0) {
  327.             lowerBoundY = adjustedConvexHullGeoXYBBox.getYMax();
  328.         }
  329.  
  330.         if (lowerBoundY.compareTo(upperBoundY) > 0) {
  331.             upperBoundY = lowerBoundY;
  332.         }
  333.  
  334.  
  335.         BoundingBox geoBBox = new BoundingBox(lowerBoundX, lowerBoundY,
  336.                                               upperBoundX, upperBoundY);
  337.         BoundingBox gridBBox = this.coordinateTranslationService.calculageGridXYBoundingBox(true, true, axisX, axisY, geoBBox);
  338.  
  339.         BigDecimal boundX = gridBBox.getXMin();
  340.  
  341.         if (boundX.compareTo(adjustedConvexHullGridXYBBox.getXMin()) < 0) {
  342.             boundX = adjustedConvexHullGridXYBBox.getXMin();
  343.         }
  344.         if (boundX.compareTo(adjustedConvexHullGridXYBBox.getXMax()) > 0) {
  345.             boundX = adjustedConvexHullGridXYBBox.getXMax();
  346.         }
  347.  
  348.         BigDecimal boundY = gridBBox.getYMin();
  349.  
  350.         if (boundY.compareTo(adjustedConvexHullGridXYBBox.getYMin()) < 0) {
  351.             boundY = adjustedConvexHullGridXYBBox.getYMin();
  352.         }
  353.         if (boundY.compareTo(adjustedConvexHullGridXYBBox.getYMax()) > 0) {
  354.             boundY = adjustedConvexHullGridXYBBox.getYMax();
  355.         }
  356.        
  357.         return new Pair<>(boundX.toPlainString(), boundY.toPlainString());
  358.     }
  359.  
  360.     /**
  361.      * Translate from the coordinates with geo-axes order (e.g: Time, Lat, Long
  362.      * with values: "1950-01-01T20:30:30Z" 25.25 35.67) to grid coordinates with
  363.      * grid-axes order (e.g: Long, Lat, Time with values: 30 40 0)
  364.      *
  365.      * @param metadata WcpsCoverageMetadata object
  366.      * @param axisNames: list of axes attending to WKT coordinates
  367.      * @param geoCoordinateArray Array of geo coordinates in geo-axes order
  368.      * @param wktCRS optional parameter to transform XY coordinates in WKT from
  369.      * input CRS to coverage's native XY axes CRS.
  370.      * @return A string representing translated coordinates in grid-axes order
  371.      */
  372.     protected String translateGeoToGridCoorinates(Map<String, Pair<BigDecimal, BigDecimal>> clippedCoverageAxesGeoBounds,
  373.                                                   WcpsCoverageMetadata metadata, List<String> axisNames,
  374.                                                   String[] geoCoordinateArray, String wktCRS,
  375.                                                   BoundingBox convexHullXYBBox) throws PetascopeException {
  376.         int geoCoordinateXOrder = -1;
  377.         int geoCoordinateYOrder = -1;
  378.        
  379.         // Check if WKT contains XY axes from input specific axisNames
  380.         boolean hasXYAxesInWKT = false;
  381.         if (metadata.hasXYAxes()) {
  382.             List<Axis> axes = metadata.getXYAxes();
  383.             Axis axisX = axes.get(0);
  384.             Axis axisY = axes.get(1);
  385.             boolean hasXAxisInWKT = false;
  386.             boolean hasYAxisInWKT = false;
  387.            
  388.             for (String axisName : axisNames) {
  389.                 if (CrsUtil.axisLabelsMatch(axisName, axisX.getLabel())) {
  390.                     hasXAxisInWKT = true;
  391.                 } else if (CrsUtil.axisLabelsMatch(axisName, axisY.getLabel())) {
  392.                     hasYAxisInWKT = true;
  393.                 }
  394.                 if (hasXAxisInWKT && hasYAxisInWKT) {
  395.                     hasXYAxesInWKT = true;
  396.                     break;
  397.                 }
  398.                
  399.             }
  400.            
  401.             geoCoordinateXOrder = this.getAxisOrder(axisNames, axisX.getLabel());
  402.             geoCoordinateYOrder = this.getAxisOrder(axisNames, axisY.getLabel());
  403.         }
  404.        
  405.         List<Pair<String, Integer>> translatedGridCoordinates = new ArrayList<>();
  406.         // Then, translate non XY-axes geo coordinate as single coordinate
  407.         for (int i = 0; i < axisNames.size(); i++) {
  408.             String axisName = axisNames.get(i);
  409.            
  410.             if (!hasXYAxesInWKT || (i != geoCoordinateXOrder && i != geoCoordinateYOrder)) {
  411.                 // Non XY-axes, translate as 1 point in 1 axis normally                
  412.                 Axis axis = metadata.getAxisByName(axisName);
  413.                 String geoCoordinate = geoCoordinateArray[i];
  414.                 BigDecimal numericGeoCoordinate = this.getNumericGeoCoordinate(metadata, axis, geoCoordinate);
  415.                 String gridCoordinate = this.translateGeoToGridPointCoordinate(axis, numericGeoCoordinate);
  416.                 translatedGridCoordinates.add(new Pair<>(gridCoordinate, axis.getRasdamanOrder()));
  417.  
  418.                 // update geo bound for the clipped output
  419.                 this.updateGeoBoundsClippedOutput(clippedCoverageAxesGeoBounds, geoCoordinateArray.length, axisName, numericGeoCoordinate);
  420.             } else {
  421.                 // XY axes, translate as a pair of geo coordinates in 2 axes (e.g: Lat, Long)
  422.                 Axis axisX = metadata.getXYAxes().get(0);
  423.                 Axis axisY = metadata.getXYAxes().get(1);
  424.  
  425.                 String geoCoordinateX = geoCoordinateArray[geoCoordinateXOrder];
  426.                 String geoCoordinateY = geoCoordinateArray[geoCoordinateYOrder];
  427.                
  428.                 if (needToSwapXYCoordinates(wktCRS, axisX.getNativeCrsUri())) {
  429.                     String tmp = geoCoordinateX;
  430.                     geoCoordinateX = geoCoordinateY;
  431.                     geoCoordinateY = tmp;
  432.                 }
  433.                
  434.                 BigDecimal numericGeoCoordinateX = this.getNumericGeoCoordinate(metadata, axisX, geoCoordinateX);
  435.                 BigDecimal numericGeoCoordinateY = this.getNumericGeoCoordinate(metadata, axisY, geoCoordinateY);
  436.                 // (e.g: WKT in EPSG:4326, native coverage XY axes is EPSG:3857, then XY coordinates in WKT are translated to EPSG:3857)
  437.                 if (wktCRS == null) {
  438.                     wktCRS = axisX.getNativeCrsUri();
  439.                 }
  440.  
  441.                 Subset subsetX = new Subset(new NumericSlicing(numericGeoCoordinateX), wktCRS, axisX.getLabel());
  442.                 Subset subsetY = new Subset(new NumericSlicing(numericGeoCoordinateY), wktCRS, axisY.getLabel());
  443.  
  444.                 List<Subset> subsets = new ArrayList<>();
  445.                 subsets.add(subsetX);
  446.                 subsets.add(subsetY);
  447.                 // Transform from subsettingCRS of WKT to native coverage CRS if necessary
  448.                 this.wcpsCoverageMetadataGeneralService.transformSubsettingCrsXYSubsets(metadata, subsets);
  449.  
  450.                 Pair<String, String> boundsPair = this.calculateXYGridBounds(clippedCoverageAxesGeoBounds, metadata,
  451.                                                                              geoCoordinateArray, subsets, convexHullXYBBox);
  452.  
  453.                 translatedGridCoordinates.add(new Pair<>(boundsPair.fst, axisX.getRasdamanOrder()));
  454.                 translatedGridCoordinates.add(new Pair<>(boundsPair.snd, axisY.getRasdamanOrder()));
  455.                 i++;
  456.             }
  457.         }
  458.         // NOTE: input WKT with coverage's geo axes order (e.g: Lat, Long, time), output with rasdaman's grid axes order (time, Lat, Long).
  459.         // e.g: with geo axes order is: 300 400 500, output in grid axes order is: 500 400 300.
  460.         String output = this.gridCoordinatesToString(translatedGridCoordinates);
  461.  
  462.         return output;
  463.     }
  464.  
  465.     /**
  466.      * From a List of translated grid coordinates but in geo axes order, sort
  467.      * this list by grid axes order and return a string // e.g: geo-axes order
  468.      * is: Lat, Long, Time (0, 20, 30) // grid-axes oder is: Time, Lat, Long
  469.      * (30, 0, 20)
  470.      *
  471.      * @return coordinates in grid-axes order
  472.      */
  473.     protected String gridCoordinatesToString(List<Pair<String, Integer>> translatedGridCoordinatesList) {
  474.         for (int i = 0; i < translatedGridCoordinatesList.size(); i++) {
  475.             for (int j = 0; j < translatedGridCoordinatesList.size(); j++) {
  476.                 Pair<String, Integer> ai = translatedGridCoordinatesList.get(i);
  477.                 Pair<String, Integer> aj = new Pair<>(translatedGridCoordinatesList.get(j).fst, translatedGridCoordinatesList.get(j).snd);
  478.                 if (ai.snd < aj.snd) {
  479.                     Pair<String, Integer> temp = new Pair<>(translatedGridCoordinatesList.get(j).fst, translatedGridCoordinatesList.get(j).snd);
  480.                     translatedGridCoordinatesList.set(j, ai);
  481.                     translatedGridCoordinatesList.set(i, temp);
  482.                 }
  483.             }
  484.         }
  485.  
  486.         List<String> tmpList = new ArrayList<>();
  487.         for (int i = 0; i < translatedGridCoordinatesList.size(); i++) {
  488.             tmpList.add(translatedGridCoordinatesList.get(i).fst);
  489.         }
  490.  
  491.         String result = ListUtil.join(tmpList, " ").trim();
  492.  
  493.         return result;
  494.     }
  495.  
  496.     /**
  497.      * Used only to get the sdom() of a clip query (e.g: oblique polygon,
  498.      * linestring) which is impossible to determine the domains of the output.
  499.      *
  500.      * @TODO: It is hard to get the sdom() of Linestring of oblique polygon in Petascope but
  501.      * if it can be then it is better than send a query to Rasql to get this value. Need more discussions about it.
  502.      *
  503.      * @return the domains (lowerBound, upperBound) for each grid axis of
  504.      * clipped output.
  505.      */
  506.     protected List<Pair<String, String>> getSdomOfClippedOutput(String clipRasqlQuery) {
  507.         // First, create a full Rasql query from the input clipping main part
  508.         // e.g: clip(c, POLYGON(( ... )) ) -> select sdom(clip(c, POLYGON(( ... )) ) from test_mean_summer_airtemp as c
  509.         List<Pair<String, String>> pairs = new ArrayList<>();
  510.        
  511.         // all the possible coverages and alias comes from FOR clause of WCPS query
  512.         String aliasRasdamanCollectionNames = this.coverageAliasRegistry.getRasqlFromClause();
  513.         String rasqlQuery = "Select sdom(" + clipRasqlQuery + ") " + aliasRasdamanCollectionNames;
  514.         try {
  515.             // e.g: [0:20,0:50]
  516.             Pair<String, String> userPair = AuthenticationService.getBasicAuthCredentialsOrRasguest(httpServletRequest);
  517.             String sdom = new String(RasUtil.getRasqlResultAsBytes(rasqlQuery, userPair.fst, userPair.snd));
  518.             sdom = sdom.replace(RASQL_OPEN_SUBSETS, "").replace(RASQL_CLOSE_SUBSETS, "");
  519.             String[] tmpArray = sdom.split(",");
  520.             for (String tmp : tmpArray) {
  521.                 String[] domain = tmp.split(RASQL_BOUND_SEPARATION);
  522.                 Pair<String, String> pair = new Pair<>(domain[0], domain[1]);
  523.                 pairs.add(pair);
  524.             }
  525.         } catch (PetascopeException ex) {
  526.             throw new WcpsRasqlException(rasqlQuery, ex.getExceptionText(), ex);
  527.         }
  528.         return pairs;
  529.     }
  530.    
  531.     /**
  532.      * After clipping, the axes' domains are reduced not as before, so it needs to update them correspondingly.
  533.      */
  534.     protected void updateOuputCoverageGeoAxesDomains(Map<String, Pair<BigDecimal, BigDecimal>> clippedCoverageAxesGeoBounds, WcpsCoverageMetadata metadata) throws PetascopeException {
  535.         // e.g: clip 2D polygon on a 2D coverage,
  536.         // it needs to update the bounding box of output coverage based on the bounding box of clipping polygon.
  537.         List<WcpsSubsetDimension> subsetDimensions = new ArrayList<>();
  538.         List<Subset> numericSubsets = new ArrayList<>();
  539.        
  540.         // NOTE: in case of clipping with curtain, only 2 axes from 3D+ coverage will attend to WKT's vertices' coordinates
  541.         // not like plain clipping with WKT when a vertex's coordinate requires all axes of coverage's expression to compound.
  542.         for (String axisName : clippedCoverageAxesGeoBounds.keySet()) {            
  543.             BigDecimal minBound = clippedCoverageAxesGeoBounds.get(axisName).fst;
  544.             BigDecimal maxBound = clippedCoverageAxesGeoBounds.get(axisName).snd;
  545.  
  546.             Axis axis = metadata.getAxisByName(axisName);
  547.  
  548.             NumericSubset numericSubset = null;
  549.             if (minBound.equals(maxBound)) {
  550.                 numericSubset = new NumericSlicing(minBound);
  551.                 subsetDimensions.add(new WcpsSliceSubsetDimension(axisName, axis.getNativeCrsUri(), minBound.toPlainString()));
  552.             } else {
  553.                 subsetDimensions.add(new WcpsTrimSubsetDimension(axisName, axis.getNativeCrsUri(), minBound.toPlainString(), maxBound.toPlainString()));
  554.                 numericSubset = new NumericTrimming(minBound, maxBound);
  555.             }
  556.             Subset subset = new Subset(numericSubset, axis.getNativeCrsUri(), axis.getLabel());
  557.             numericSubsets.add(subset);
  558.         }
  559.        
  560.         // Update clipped coverage expression with the new subsets from WKT shape
  561.         // e.g: original coverage has axis with geo bounds: Lat(0, 20), Long(0, 30) and WKT polygon has a bounding box is Lat(0:5), Long(20:25)
  562.         // then output is a coverage with bounding box in geo bounds: Lat(0:5), Long(20:25)
  563.         // NOTE: coordinates of vertices of WKT can be out of coverage's bounding box (not throw exception in this case)
  564.         this.wcpsCoverageMetadataGeneralService.applySubsets(false, true, metadata, subsetDimensions, numericSubsets);
  565.     }
  566.    
  567.     /**
  568.      * Handle the clip operator and input WKT to be
  569.      * applied on current coverage expression and generate Rasql result for it.
  570.      */
  571.     protected WcpsResult mainHandle(Map<String, Pair<BigDecimal, BigDecimal>> clippedCoverageAxesGeoBounds, WcpsResult coverageExpression, List<String> axisNames,
  572.                                     AbstractWKTShape wktShape, String wktCRS, String rasqlTemplate) throws PetascopeException {
  573.         checkOperandIsCoverage(coverageExpression, OPERATOR);
  574.        
  575.         WcpsCoverageMetadata metadata = coverageExpression.getMetadata();
  576.         // Translate geo coordinates to grid coordinates for XY axes in pair and other axis individually
  577.         List<String> finalTranslatedWKTCompoundPointsList = new ArrayList<>();
  578.  
  579.         List<WKTCompoundPoints> wktCompoundPointsList = wktShape.getWktCompoundPointsList();
  580.         for (int i = 0; i < wktCompoundPointsList.size(); i++) {
  581.             WKTCompoundPoints wktCompoundPoints = wktCompoundPointsList.get(i);
  582.             List<String> translatedWKTCompoudPointsList = new ArrayList<>();
  583.            
  584.             for (int j = 0; j < wktCompoundPoints.getWKTCompoundPoints().size(); j++) {
  585.                 // e.g: 20.5 30.5 "2008-01-01T02:01:20.000Z",40.5 50.5 "2008-01-03T23:59:55.000Z"
  586.                 WKTCompoundPoint wktCompoundPoint = wktCompoundPoints.getWKTCompoundPoints().get(j);
  587.                 // e.g: geoPoints[0] = 20.5 30.5 "2008-01-01T02:01:20.000Z", geoPoints[1] = 40.5 50.5 "2008-01-03T23:59:55.000Z"
  588.                 String[] geoPoints = wktCompoundPoint.getPoint().split(",");
  589.                 BoundingBox convexHullXYBBox = this.getConvexHullXYBoundingBox(metadata, geoPoints, axisNames, wktCRS);
  590.                 List<String> translatedGeoPointsList = new ArrayList<>();
  591.  
  592.                 String previousTranslatedGridPointCoordinates = "";
  593.                 // each pointCoordinate belongs to one coverage's axis
  594.                 for (int k = 0; k < geoPoints.length; k++) {
  595.                     // e.g: geoPointCoordinates = [20, 30, "2008-01-01T02:01:20.000Z"]
  596.                     String[] geoPointCoordinates = geoPoints[k].split(" ");
  597.                     String translatedGridPointCoordinates;
  598.                     if (wktCRS != null && wktCRS.equals(CrsUtil.GRID_CRS)) {
  599.                         // e.g: clip(c, POLYGON((...)), "CRS:1"), then no need to translate any coordinates in WKT
  600.                         translatedGridPointCoordinates = geoPoints[k];
  601.                     } else {
  602.                         // e.g: clip(c, POLYGON((...)) ) or clip(c, POLYGON((...)), "http://opengis.net/def/crs/EPSG/0/3857")
  603.                         // then need to translate geo coordinates in WKT to grid coordinates accordingly
  604.                         // This is the translated grid coordinates in grid-axes order to query in rasql
  605.                         translatedGridPointCoordinates = this.translateGeoToGridCoorinates(clippedCoverageAxesGeoBounds, metadata,
  606.                                                                                            axisNames, geoPointCoordinates, wktCRS,
  607.                                                                                            convexHullXYBBox);
  608.                     }
  609.                     if (!previousTranslatedGridPointCoordinates.equals(translatedGridPointCoordinates)) {
  610.                         // NOTE: don't add the duplicate grid coordinates to Rasql as they are redundant and cause significant slow in rasserver
  611.                         // e.g: 20 30, 20 30, 20 30, 30 40 then, only add 20 30, 30 40 as grid coordinates
  612.                         translatedGeoPointsList.add(translatedGridPointCoordinates);
  613.                     }
  614.                     previousTranslatedGridPointCoordinates = translatedGridPointCoordinates;
  615.                 }
  616.                
  617.                 if (translatedGeoPointsList.size() == 1) {
  618.                     String firstValue = translatedGeoPointsList.get(0);
  619.                     translatedGeoPointsList.add(firstValue);
  620.                 }
  621.                 // e.g: 20 30 40,30 40 50,60 70 80
  622.                 String translatedGeoPoints = ListUtil.join(translatedGeoPointsList, ",");
  623.                 translatedWKTCompoudPointsList.add("(" + translatedGeoPoints + ")");
  624.             }
  625.             String tmp = ListUtil.join(translatedWKTCompoudPointsList, ",");
  626.             finalTranslatedWKTCompoundPointsList.add("(" + tmp + ")");
  627.         }
  628.  
  629.         // e.g: (20 30 40,30 40 50,60 70 80),(20 50 60,70 80 90,50 60 70)
  630.         String translatedCoordinates = ListUtil.join(finalTranslatedWKTCompoundPointsList, ",");
  631.         String translatedWKT = wktShape.getStringRepresentation(translatedCoordinates);
  632.  
  633.         // e.g: clip( c, POLYGON((30 50, 60 70, 80 90),(120 150, 160 170, 190 220)) )
  634.         String rasql = rasqlTemplate.replace(TRANSLATED_COVERAGE_EXPRESSION_RASQL_TEMPLATE, coverageExpression.getRasql())
  635.                                     .replace(TRANSLATED_WKT_EXPRESSION_RASQL_TEMPLATE, translatedWKT);
  636.  
  637.         // NOTE: it just finished the main task, subclasses needs to finish the rests for different clipping types.
  638.         WcpsResult result = new WcpsResult(metadata, rasql);
  639.  
  640.         return result;
  641.     }
  642. }
Tags: Java
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement