private static String guessCommonSRS(List<MapLayerInfo> layers) {
   String SRS = null;
   for (MapLayerInfo layer : layers) {
     String layerSRS = layer.getSRS();
     if (SRS == null) {
       SRS = layerSRS.toUpperCase();
     } else if (!SRS.equals(layerSRS)) {
       // layers with mixed native SRS, let's just use the default
       return DefaultWebMapService.SRS;
     }
   }
   if (SRS == null) {
     return DefaultWebMapService.SRS;
   }
   return SRS;
 }
  /*
   * Adds a feature layer to the geopackage.
   */
  void addFeatureLayer(
      GeoPackage geopkg, FeatureLayer layer, MapLayerInfo mapLayer, WMSMapContent map)
      throws IOException {

    FeatureEntry e = new FeatureEntry();
    initEntry(e, layer, mapLayer, map);

    Filter filter = layer.getQuery().getFilter();
    GeometryDescriptor gd = mapLayer.getFeature().getFeatureType().getGeometryDescriptor();
    if (gd != null) {
      Envelope bnds = bounds(map);
      BBOX bboxFilter =
          filterFactory.bbox(
              gd.getLocalName(),
              bnds.getMinX(),
              bnds.getMinY(),
              bnds.getMaxX(),
              bnds.getMaxY(),
              map.getRequest().getSRS());
      filter = filterFactory.and(filter, bboxFilter);
    }

    LOGGER.fine("Creating feature entry" + e.getTableName());
    geopkg.add(e, layer.getSimpleFeatureSource(), filter);
  }
  void initEntry(Entry e, Layer layer, MapLayerInfo mapLayer, WMSMapContent map)
      throws IOException {

    ResourceInfo r = mapLayer.getResource();

    e.setTableName(r.getName());
    e.setIdentifier(r.getTitle());
    e.setDescription(r.getAbstract());
    e.setBounds(bounds(map));
    e.setSrid(srid(map));
  }
  void addCoverageLayer(
      GeoPackage geopkg, GridCoverageLayer layer, MapLayerInfo mapLayer, WMSMapContent map)
      throws IOException {

    RasterEntry e = new RasterEntry();
    initEntry(e, layer, mapLayer, map);

    // TODO: ensure this is one of the supported formats
    AbstractGridFormat format = mapLayer.getCoverage().getStore().getFormat();

    LOGGER.fine("Creating raster entry" + e.getTableName());
    geopkg.add(e, layer.getCoverage(), format);
  }
  /**
   * This method tries to automatically determine SRS, bounding box and output size based on the
   * layers provided by the user and any other parameters.
   *
   * <p>If bounds are not specified by the user, they are automatically se to the union of the
   * bounds of all layers.
   *
   * <p>The size of the output image defaults to 512 pixels, the height is automatically determined
   * based on the width to height ratio of the requested layers. This is also true if either height
   * or width are specified by the user. If both height and width are specified by the user, the
   * automatically determined bounding box will be adjusted to fit inside these bounds.
   *
   * <p>General idea 1) Figure out whether SRS has been specified, fall back to EPSG:4326 2)
   * Determine whether all requested layers use the same SRS, - if so, try to do bounding box
   * calculations in native coordinates 3) Aggregate the bounding boxes (in EPSG:4326 or native) 4a)
   * If bounding box has been specified, adjust height of image to match 4b) If bounding box has not
   * been specified, but height has, adjust bounding box
   */
  public static void autoSetBoundsAndSize(GetMapRequest getMap) {
    // Get the layers
    List<MapLayerInfo> layers = getMap.getLayers();

    /** 1) Check what SRS has been requested */
    String reqSRS = getMap.getSRS();

    // if none, try to determine which SRS to use
    // and keep track of whether we can use native all the way
    boolean useNativeBounds = true;
    if (reqSRS == null) {
      reqSRS = guessCommonSRS(layers);
      forceSRS(getMap, reqSRS);
    }

    /** 2) Compare requested SRS */
    for (int i = 0; useNativeBounds && i < layers.size(); i++) {
      if (layers.get(i) != null) {
        String layerSRS = layers.get(i).getSRS();
        useNativeBounds =
            reqSRS.equalsIgnoreCase(layerSRS)
                && layers.get(i).getResource().getNativeBoundingBox() != null;
      } else {
        useNativeBounds = false;
      }
    }

    CoordinateReferenceSystem reqCRS;
    try {
      reqCRS = CRS.decode(reqSRS);
    } catch (Exception e) {
      throw new ServiceException(e);
    }

    // Ready to determine the bounds based on the layers, if not specified
    Envelope aggregateBbox = getMap.getBbox();
    boolean specifiedBbox = true;

    // If bbox is not specified by request
    if (aggregateBbox == null) {
      specifiedBbox = false;

      // Get the bounding box from the layers
      for (int i = 0; i < layers.size(); i++) {
        MapLayerInfo layerInfo = layers.get(i);
        ReferencedEnvelope curbbox;
        try {
          curbbox = layerInfo.getLatLongBoundingBox();
          if (useNativeBounds) {
            ReferencedEnvelope nativeBbox = layerInfo.getBoundingBox();
            if (nativeBbox == null) {
              try {
                CoordinateReferenceSystem nativeCrs = layerInfo.getCoordinateReferenceSystem();
                nativeBbox = curbbox.transform(nativeCrs, true);
              } catch (Exception e) {
                throw new ServiceException("Best effort native bbox computation failed", e);
              }
            }
            curbbox = nativeBbox;
          }
        } catch (Exception e) {
          throw new RuntimeException(e);
        }
        if (aggregateBbox != null) {
          aggregateBbox.expandToInclude(curbbox);
        } else {
          aggregateBbox = curbbox;
        }
      }

      ReferencedEnvelope ref = null;
      // Reproject back to requested SRS if we have to
      if (!useNativeBounds && !reqSRS.equalsIgnoreCase(SRS)) {
        try {
          ref = new ReferencedEnvelope(aggregateBbox, CRS.decode("EPSG:4326"));
          aggregateBbox = ref.transform(reqCRS, true);
        } catch (ProjectionException pe) {
          ref.expandBy(-1 * ref.getWidth() / 50, -1 * ref.getHeight() / 50);
          try {
            aggregateBbox = ref.transform(reqCRS, true);
          } catch (FactoryException e) {
            e.printStackTrace();
          } catch (TransformException e) {
            e.printStackTrace();
          }
          // And again...
        } catch (NoSuchAuthorityCodeException e) {
          e.printStackTrace();
        } catch (TransformException e) {
          e.printStackTrace();
        } catch (FactoryException e) {
          e.printStackTrace();
        }
      }
    }

    // Just in case
    if (aggregateBbox == null) {
      forceSRS(getMap, DefaultWebMapService.SRS);
      aggregateBbox = DefaultWebMapService.BBOX;
    }

    // Start the processing of adjust either the bounding box
    // or the pixel height / width

    double bbheight = aggregateBbox.getHeight();
    double bbwidth = aggregateBbox.getWidth();
    double bbratio = bbwidth / bbheight;

    double mheight = getMap.getHeight();
    double mwidth = getMap.getWidth();

    if (mheight > 0.5 && mwidth > 0.5 && specifiedBbox) {
      // This person really doesnt want our help,
      // we'll warp it any way they like it...
    } else {
      if (mheight > 0.5 && mwidth > 0.5) {
        // Fully specified, need to adjust bbox
        double mratio = mwidth / mheight;
        // Adjust bounds to be less than ideal to meet spec
        if (bbratio > mratio) {
          // Too wide, need to increase height of bb
          double diff = ((bbwidth / mratio) - bbheight) / 2;
          aggregateBbox.expandBy(0, diff);
        } else {
          // Too tall, need to increase width of bb
          double diff = ((bbheight * mratio) - bbwidth) / 2;
          aggregateBbox.expandBy(diff, 0);
        }

        adjustBounds(reqSRS, aggregateBbox);

      } else if (mheight > 0.5) {
        mwidth = bbratio * mheight;
      } else {
        if (mwidth > 0.5) {
          mheight = (mwidth / bbratio >= 1) ? mwidth / bbratio : 1;
        } else {
          if (bbratio > 1) {
            mwidth = MAX_SIDE;
            mheight = (mwidth / bbratio >= 1) ? mwidth / bbratio : 1;
          } else {
            mheight = MAX_SIDE;
            mwidth = (mheight * bbratio >= 1) ? mheight * bbratio : 1;
          }

          // make sure OL output height is sufficient to show the OL scale bar fully
          if (mheight < MIN_OL_HEIGHT
              && ("application/openlayers".equalsIgnoreCase(getMap.getFormat())
                  || "openlayers".equalsIgnoreCase(getMap.getFormat()))) {
            mheight = MIN_OL_HEIGHT;
            mwidth = (mheight * bbratio >= 1) ? mheight * bbratio : 1;
          }
        }
      }

      // Actually set the bounding box and size of image
      getMap.setBbox(aggregateBbox);
      getMap.setWidth((int) mwidth);
      getMap.setHeight((int) mheight);
    }
  }
  @SuppressWarnings({"rawtypes", "unchecked"})
  @Override
  public Object read(Object req, Map kvp, Map rawKvp) throws Exception {
    GetFeatureInfoRequest request = (GetFeatureInfoRequest) super.read(req, kvp, rawKvp);
    request.setRawKvp(rawKvp);

    GetMapRequest getMapPart = new GetMapRequest();
    try {
      getMapPart = getMapReader.read(getMapPart, kvp, rawKvp);
    } catch (ServiceException se) {
      throw se;
    } catch (Exception e) {
      throw new ServiceException(e);
    }

    request.setGetMapRequest(getMapPart);

    List<MapLayerInfo> getMapLayers = getMapPart.getLayers();

    if ((getMapPart.getSldBody() != null || getMapPart.getSld() != null)
        && (rawKvp.get("QUERY_LAYERS") == null || "".equals(rawKvp.get("QUERY_LAYERS")))) {
      // in this case we assume all layers in SLD body are to be queried (GS own extension)(
      request.setQueryLayers(getMapLayers);
    } else {
      request.setQueryLayers(
          new MapLayerInfoKvpParser("QUERY_LAYERS", wms)
              .parse((String) rawKvp.get("QUERY_LAYERS")));
    }

    if (request.getQueryLayers().isEmpty()) {
      throw new ServiceException(
          "No QUERY_LAYERS has been requested, or no " + "queriable layer in the request anyways");
    }

    if (kvp.containsKey("propertyName")) {
      List<List<String>> propertyNames = (List<List<String>>) kvp.get("propertyName");
      if (propertyNames.size() == 1 && request.getQueryLayers().size() > 1) {
        // assume we asked the same list for all layers
        while (propertyNames.size() < request.getQueryLayers().size()) {
          propertyNames.add(propertyNames.get(0));
        }
      }
      if (propertyNames.size() != request.getQueryLayers().size()) {
        throw new ServiceException(
            "Mismatch between the property name set count "
                + propertyNames.size()
                + " and the query layers count "
                + request.getQueryLayers().size(),
            "InvalidParameter",
            "propertyName");
      }
      request.setPropertyNames(propertyNames);
    }

    // make sure they are a subset of layers
    List<MapLayerInfo> queryLayers = new ArrayList<MapLayerInfo>(request.getQueryLayers());
    queryLayers.removeAll(getMapLayers);
    if (queryLayers.size() > 0) {
      // we've already expanded base layers so let's avoid list the names, they are not
      // the original ones anymore
      throw new ServiceException(
          "QUERY_LAYERS contains layers not cited in LAYERS. "
              + "It should be a proper subset of those instead");
    }

    for (MapLayerInfo l : request.getQueryLayers()) {
      LayerInfo layerInfo = l.getLayerInfo();
      if (!wms.isQueryable(layerInfo)) {
        throw new ServiceException(
            "Layer " + l.getName() + " is not queryable",
            WMSErrorCode.LAYER_NOT_QUERYABLE.get(request.getVersion()),
            "QUERY_LAYERS");
      }
    }

    String format = (String) (kvp.containsKey("INFO_FORMAT") ? kvp.get("INFO_FORMAT") : null);

    if (format == null) {
      format = "text/plain";
    } else {
      List<String> infoFormats = wms.getAvailableFeatureInfoFormats();
      if (!infoFormats.contains(format)) {
        throw new ServiceException(
            "Invalid format '" + format + "', supported formats are " + infoFormats,
            "InvalidFormat",
            "info_format");
      }
      if (wms.getAllowedFeatureInfoFormats().contains(format) == false)
        throw wms.unallowedGetFeatureInfoFormatException(format);
    }

    request.setInfoFormat(format);

    request.setFeatureCount(1); // DJB: according to the WMS spec (7.3.3.7 FEATURE_COUNT) this
    // should be 1. also tested for by cite
    try {
      int maxFeatures = Integer.parseInt(String.valueOf(kvp.get("FEATURE_COUNT")));
      request.setFeatureCount(maxFeatures);
    } catch (NumberFormatException ex) {
      // do nothing, FEATURE_COUNT is optional
    }

    Version version = wms.negotiateVersion(request.getVersion());
    request.setVersion(version.toString());

    // JD: most wms 1.3 client implementations still use x/y rather than i/j, so we support those
    // too when i/j not specified when not running in strict cite compliance mode
    String colPixel, rowPixel;
    if (version.compareTo(WMS.VERSION_1_3_0) >= 0) {
      colPixel = "I";
      rowPixel = "J";

      if (!kvp.containsKey(colPixel) && !kvp.containsKey(rowPixel)) {
        if (!wms.getServiceInfo().isCiteCompliant()
            && kvp.containsKey("X")
            && kvp.containsKey("Y")) {
          colPixel = "X";
          rowPixel = "Y";
        }
      }
    } else {
      colPixel = "X";
      rowPixel = "Y";
    }

    try {
      String colParam = String.valueOf(kvp.get(colPixel));
      String rowParam = String.valueOf(kvp.get(rowPixel));
      int x = Integer.parseInt(colParam);
      int y = Integer.parseInt(rowParam);

      // ensure x/y in dimension of image
      if (x < 0 || x > getMapPart.getWidth() || y < 0 || y > getMapPart.getHeight()) {
        throw new ServiceException(
            String.format(
                "%d, %d not in dimensions of image: %d, %d",
                x, y, getMapPart.getWidth(), getMapPart.getHeight()),
            "InvalidPoint");
      }
      request.setXPixel(x);
      request.setYPixel(y);
    } catch (NumberFormatException ex) {
      String msg = colPixel + " and " + rowPixel + " incorrectly specified";
      throw new ServiceException(msg, "InvalidPoint");
    }

    return request;
  }
  GridSubset findBestGridSubset(WMSMapContent map) {
    GetMapRequest req = map.getRequest();
    Map formatOpts = req.getFormatOptions();

    GridSetBroker gridSetBroker = gwc.getGridSetBroker();
    GridSet gridSet = null;

    // first check format options to see if explicitly specified
    if (formatOpts.containsKey("gridset")) {
      gridSet = gridSetBroker.get(formatOpts.get("gridset").toString());
    }

    // next check srs
    if (gridSet == null) {
      gridSet = gridSetBroker.get(req.getSRS().toUpperCase());
    }

    if (gridSet != null) {
      return GridSubsetFactory.createGridSubSet(gridSet);
    }

    CoordinateReferenceSystem crs = map.getCoordinateReferenceSystem();

    // look up epsg code
    Integer epsgCode = null;
    try {
      epsgCode = CRS.lookupEpsgCode(crs, false);
    } catch (Exception e) {
      throw new ServiceException("Unable to determine epsg code for " + crs, e);
    }
    if (epsgCode == null) {
      throw new ServiceException("Unable to determine epsg code for " + crs);
    }

    SRS srs = SRS.getSRS(epsgCode);

    // figure out the appropriate grid sub set
    Set<GridSubset> gridSubsets = new LinkedHashSet<GridSubset>();
    for (MapLayerInfo l : req.getLayers()) {
      TileLayer tl = gwc.getTileLayerByName(l.getName());
      if (tl == null) {
        throw new ServiceException("No tile layer for " + l.getName());
      }

      List<GridSubset> theseGridSubsets = tl.getGridSubsetsForSRS(srs);
      if (gridSubsets.isEmpty()) {
        gridSubsets.addAll(theseGridSubsets);
      } else {
        gridSubsets.retainAll(theseGridSubsets);
      }

      if (gridSubsets.isEmpty()) {
        throw new ServiceException(
            "No suitable " + epsgCode + " grid subset for " + req.getLayers());
      }
    }

    if (gridSubsets.size() > 1) {
      if (LOGGER.isLoggable(Level.WARNING)) {
        StringBuilder msg = new StringBuilder("Found multiple grid subsets: ");
        for (GridSubset gs : gridSubsets) {
          msg.append(gs.getName()).append(", ");
        }
        msg.setLength(msg.length() - 2);
        msg.append(". Choosing first.");
        LOGGER.warning(msg.toString());
      }
    }

    return gridSubsets.iterator().next();
  }
Example #8
0
  /**
   * Returns the read parameters for the specified layer, merging some well known request parameters
   * into the read parameters if possible
   *
   * @param request
   * @param mapLayerInfo
   * @param layerFilter
   * @param reader
   * @return
   */
  public GeneralParameterValue[] getWMSReadParameters(
      final GetMapRequest request,
      final MapLayerInfo mapLayerInfo,
      final Filter layerFilter,
      final List<Object> times,
      final List<Object> elevations,
      final GridCoverage2DReader reader,
      boolean readGeom)
      throws IOException {
    // setup the scene
    final ParameterValueGroup readParametersDescriptor = reader.getFormat().getReadParameters();
    CoverageInfo coverage = mapLayerInfo.getCoverage();
    MetadataMap metadata = coverage.getMetadata();
    GeneralParameterValue[] readParameters =
        CoverageUtils.getParameters(readParametersDescriptor, coverage.getParameters(), readGeom);
    ReaderDimensionsAccessor dimensions = new ReaderDimensionsAccessor(reader);
    // pass down time
    final DimensionInfo timeInfo = metadata.get(ResourceInfo.TIME, DimensionInfo.class);
    // add the descriptors for custom dimensions
    final List<GeneralParameterDescriptor> parameterDescriptors =
        new ArrayList<GeneralParameterDescriptor>(
            readParametersDescriptor.getDescriptor().descriptors());
    Set<ParameterDescriptor<List>> dynamicParameters = reader.getDynamicParameters();
    parameterDescriptors.addAll(dynamicParameters);
    if (timeInfo != null && timeInfo.isEnabled()) {
      // handle "default"
      List<Object> fixedTimes = new ArrayList<Object>(times);
      for (int i = 0; i < fixedTimes.size(); i++) {
        if (fixedTimes.get(i) == null) {
          fixedTimes.set(i, getDefaultTime(coverage));
        }
      }
      // pass down the parameters
      readParameters =
          CoverageUtils.mergeParameter(
              parameterDescriptors, readParameters, fixedTimes, "TIME", "Time");
    }

    // pass down elevation
    final DimensionInfo elevationInfo = metadata.get(ResourceInfo.ELEVATION, DimensionInfo.class);
    if (elevationInfo != null && elevationInfo.isEnabled()) {
      // handle "default"
      List<Object> fixedElevations = new ArrayList<Object>(elevations);
      for (int i = 0; i < fixedElevations.size(); i++) {
        if (fixedElevations.get(i) == null) {
          fixedElevations.set(i, getDefaultElevation(coverage));
        }
      }
      readParameters =
          CoverageUtils.mergeParameter(
              parameterDescriptors, readParameters, fixedElevations, "ELEVATION", "Elevation");
    }

    if (layerFilter != null && readParameters != null) {
      // test for default [empty is replaced with INCLUDE filter] ]filter
      for (int i = 0; i < readParameters.length; i++) {

        GeneralParameterValue param = readParameters[i];
        GeneralParameterDescriptor pd = param.getDescriptor();

        if (pd.getName().getCode().equalsIgnoreCase("FILTER")) {
          final ParameterValue pv = (ParameterValue) pd.createValue();
          // if something different from the default INCLUDE filter is specified
          if (layerFilter != Filter.INCLUDE) {
            // override the default filter
            pv.setValue(layerFilter);
            readParameters[i] = pv;
          }
          break;
        }
      }
    }

    // custom dimensions

    List<String> customDomains = new ArrayList(dimensions.getCustomDomains());
    for (String domain : new ArrayList<String>(customDomains)) {
      List<String> values = request.getCustomDimension(domain);
      if (values != null) {
        readParameters =
            CoverageUtils.mergeParameter(parameterDescriptors, readParameters, values, domain);
        customDomains.remove(domain);
      }
    }

    // see if we have any custom domain for which we have to set the default value
    if (!customDomains.isEmpty()) {
      for (String name : customDomains) {
        final DimensionInfo customInfo =
            metadata.get(ResourceInfo.CUSTOM_DIMENSION_PREFIX + name, DimensionInfo.class);
        if (customInfo != null && customInfo.isEnabled()) {
          final ArrayList<String> val = new ArrayList<String>(1);
          val.add(getDefaultCustomDimensionValue(name, coverage, String.class));
          readParameters =
              CoverageUtils.mergeParameter(parameterDescriptors, readParameters, val, name);
        }
      }
    }

    return readParameters;
  }