/* * Constellation - An open source and standard compliant SDI * http://www.constellation-sdi.org * * Copyright 2014 Geomatys. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.constellation.map.featureinfo; import org.apache.sis.util.logging.Logging; import org.constellation.configuration.ConfigurationException; import org.constellation.configuration.GFIParam; import org.constellation.configuration.GetFeatureInfoCfg; import org.constellation.provider.DataProvider; import org.constellation.provider.DataProviders; import org.constellation.provider.coveragesgroup.CoveragesGroupProvider; import org.constellation.provider.coveragesgroup.util.ConvertersJaxbToGeotk; import org.geotoolkit.display.PortrayalException; import org.geotoolkit.display.SearchArea; import org.geotoolkit.display.canvas.RenderingContext; import org.geotoolkit.display2d.GraphicVisitor; import org.geotoolkit.display2d.primitive.ProjectedObject; import org.geotoolkit.display2d.service.CanvasDef; import org.geotoolkit.display2d.service.DefaultPortrayalService; import org.geotoolkit.display2d.service.SceneDef; import org.geotoolkit.display2d.service.ViewDef; import org.geotoolkit.display2d.service.VisitDef; import org.geotoolkit.map.MapContext; import org.geotoolkit.map.MapItem; import org.geotoolkit.map.MapLayer; import org.geotoolkit.ows.xml.GetFeatureInfo; import org.opengis.display.primitive.Graphic; import javax.xml.bind.JAXBException; import java.awt.*; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import org.opengis.util.GenericName; /** * FeatureInfoFormat designed to be used with a CoverageGroup provider. * Configuration must be contain the {@link #PROVIDER_ID_PARAM} parameter filled with * layer provider id. * AggregateFeatureInfoFormat will check the CoverageGroup provider configuration to see if * layers have some custom GetFeatureInfo configured. In that case the {@link #getSupportedMimeTypes()} * method will return all configured mimeTypes. * * @author Quentin Boileau (Geomatys) */ public class AggregateFeatureInfoFormat extends AbstractFeatureInfoFormat { public static final Logger LOGGER = Logging.getLogger("org.constellation.map.featureinfo"); public static final String PROVIDER_ID_PARAM = "providerID"; private Map<String, List<GetFeatureInfoCfg>> layersConfig = null; private FeatureInfoFormat subFormat = null; @Override public List<String> getSupportedMimeTypes() { try { if (layersConfig == null) { if (!ckeckConfiguration()) return new ArrayList<>(); } final Set<String> supportedMimes = new HashSet<>(); for (List<GetFeatureInfoCfg> infoCfgs : layersConfig.values()) { for (GetFeatureInfoCfg gfiCfg : infoCfgs) { if (gfiCfg.getMimeType() != null && !(gfiCfg.getMimeType().isEmpty())) { supportedMimes.add(gfiCfg.getMimeType()); } } } return new ArrayList<>(supportedMimes); } catch (ConfigurationException e) { LOGGER.log(Level.WARNING, e.getMessage(), e); } return new ArrayList<>(); } @Override public Object getFeatureInfo(final SceneDef sdef, final ViewDef vdef, final CanvasDef cdef, final Rectangle searchArea, final GetFeatureInfo getFI) throws PortrayalException { try { if (!ckeckConfiguration()) return null; } catch (ConfigurationException e) { throw new PortrayalException(e.getMessage(), e); } final String requestMime = getFI.getInfoFormat(); final VisitDef visitDef = new VisitDef(); visitDef.setArea(searchArea); visitDef.setVisitor(new GraphicVisitor() { int maxCandidat = getFeatureCount(getFI); int idx = 0; boolean stop = false; @Override public void startVisit() { } @Override public void endVisit() { } @Override public void visit(Graphic graphic, RenderingContext context, SearchArea area) { if(graphic == null ) return; if (graphic instanceof ProjectedObject) { final ProjectedObject obj = (ProjectedObject) graphic; final MapLayer layer = obj.getLayer(); final List<GetFeatureInfoCfg> gfiList = layersConfig.get(layer.getName()); if (gfiList != null && !(gfiList.isEmpty())) { for (GetFeatureInfoCfg gfiCfg : gfiList) { // mime not null and equals to request if (gfiCfg.getMimeType() != null && !(gfiCfg.getMimeType().isEmpty()) && gfiCfg.getMimeType().equals(requestMime)) { try { subFormat = FeatureInfoUtilities.getFeatureInfoFormatFromConf(gfiCfg); } catch (ClassNotFoundException | ConfigurationException e) { LOGGER.log(Level.WARNING, e.getMessage(), e); } stop = true; } } } } idx++; } @Override public boolean isStopRequested() { return !stop ? (idx == maxCandidat) : true; } }); DefaultPortrayalService.visit(cdef,sdef,vdef,visitDef); if (subFormat != null) { return subFormat.getFeatureInfo(sdef, vdef, cdef, searchArea, getFI); } return null; } /** * Check the GetFeatureInfo configuration and associated CoverageGroup provider configuration as well. * * @return true if configuration is ok and false if no configuration is set. * @throws ConfigurationException if linked provider is not a CoverageGroup instance or his configuration unmarshalling * went wrong. */ private boolean ckeckConfiguration() throws ConfigurationException { final GetFeatureInfoCfg conf = getConfiguration(); if (conf == null) { //we should never be in this case. //throw new PortrayalException("AggregatedFeatureInfoFormat configuration not found."); return false; } final List<GFIParam> gfiParams = conf.getGfiParameter(); String providerId = null; for (GFIParam gfiParam : gfiParams) { if (gfiParam.getKey().equals(PROVIDER_ID_PARAM)) { providerId = gfiParam.getValue(); } } if (providerId == null || providerId.isEmpty()) { //throw new PortrayalException("GetFeatureInfo configuration parameter "+PROVIDER_ID_PARAM+" is not defined."); return false; } final DataProviders namedProxy = DataProviders.getInstance(); final DataProvider provider = namedProxy.getProvider(providerId); if (provider == null || !(provider instanceof CoveragesGroupProvider)) { throw new ConfigurationException("GetFeatureInfo configuration parameter "+PROVIDER_ID_PARAM +" doesn't reference a CoverageGroup provider."); } final CoveragesGroupProvider cgProvider = (CoveragesGroupProvider) provider; final Set<GenericName> layers = cgProvider.getKeys(); MapContext mapContext = null; if (layers.size() > 0) { try { //only get the fist layer because usually CoverageGroup provider are linked to one MapContext file. mapContext = cgProvider.getMapContext(layers.iterator().next(), "", ""); } catch (JAXBException e) { throw new ConfigurationException("Error during MapContext unmarshalling.", e.getMessage()); } } if (mapContext == null) { throw new ConfigurationException("CoverageGroup provider "+PROVIDER_ID_PARAM +" doesn't reference a MapContext layer."); } layersConfig = new HashMap<>(); final List<MapLayer> mapLayers = getMapLayers(mapContext); for (MapLayer layer : mapLayers) { final Object original = layer.getUserProperty(ConvertersJaxbToGeotk.ORIGINAL_CONFIG); if (original instanceof org.constellation.provider.coveragesgroup.xml.MapLayer) { final org.constellation.provider.coveragesgroup.xml.MapLayer cfg = (org.constellation.provider.coveragesgroup.xml.MapLayer) original; layersConfig.put(layer.getName(), cfg.getGetFeatureInfoCfgs()); } } return true; } /** * Recursively find leaf MapItem (know as MapLayer). * @param mapContext * @return */ private List<MapLayer> getMapLayers(MapContext mapContext) { final List<MapLayer> layers = new ArrayList<>(); getMapLayers(mapContext, layers); return layers; } /** * Recursively find leaf MapItem (know as MapLayer). * @param mapItem * @param layers */ private void getMapLayers(MapItem mapItem, List<MapLayer> layers) { if (mapItem instanceof MapLayer) { layers.add((MapLayer)mapItem); } else { for (MapItem item : mapItem.items()) { getMapLayers(item, layers); } } } }