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(); }
/** * 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; }